Giter Club home page Giter Club logo

http-builder-ng's Introduction

HttpBuilder-NG: Easy HTTP Client for Groovy (and Java)

Bintray - Core Bintray - Apache Bintray - OkHttp

Maven Central - Core Maven Central - Apache Maven Central - OkHttp

Travis Build Status Coverage Status

Twitter Follow

Dormant - The HttpBuilder-NG project is going dormant. Neither of us use the project any longer nor do we have the extra time to properly maintain it. Please feel free to fork it and move it forward, or contact us (with an issue) to discuss options for taking over the project.

Quick Links for the Impatient

Quick Overview

Http Builder NG is a modern Groovy DSL for making http requests. It requires Java 8 and a modern Groovy. It is built against Groovy 2.4.x, but it doesn't make any assumptions about which version of Groovy you are using. The main goal of Http Builder NG is to allow you to make http requests in a natural and readable way. For example:

//let's configure an http client to make calls to httpbin.org using the default http library
def httpBin = HttpBuilder.configure {
    request.uri = 'http://httpbin.org/'
}

//now let's GET /get endpoint at httpbin.
//This will return a JSON formatted response with an origin property.
def result = httpBin.get {
    request.uri.path = '/get'
}
    
println("Your ip address is: ${result.origin}")

//Finally lets post a standard http form to httpbin
httpBin.post {
    request.uri.path = '/post'
    request.body = [ input1: 'the first input', input2: 'the second input' ]
    request.contentType = 'application/x-www-form-urlencoded'
}

Hopefully that gives you a general idea of how Http Builder NG works. Http Builder NG is designed to be compatible with Groovy code annotated with @TypeChecked and @CompileStatic. It also makes use of the @DelegatesTo to provide better IDE support when writing code using Http Builder NG.

Artifacts

Http Builder NG artifacts are available on Bintray and Maven Central, for Gradle you can add the following dependency to your build.gradle file dependencies closure:

compile 'io.github.http-builder-ng:http-builder-ng-CLIENT:1.0.4'

or, for Maven add the following to your pom.xml file:

<dependency>
  <groupId>io.github.http-builder-ng</groupId>
  <artifactId>http-builder-ng-CLIENT</artifactId>
  <version>1.0.4</version>
</dependency>

where CLIENT is replaced with the client library name (core, apache, or okhttp).

Build Instructions

HttpBuilder-NG is built using gradle. To perform a complete build run the following:

`./gradlew clean build`

Test reports are not automatically generated; if you need a generated test report (aggregated or per-project) use:

`./gradlew clean build jacocoTestReport aggregateCoverage`

Note that the aggregateCoverage task may be dropped if the aggregated report is not desired. The reports will be generated in their respective build/reports directories, with the aggregated report being in the build directory of the project root.

You can also generate the documentation using one of the following commands:

  • For the aggregated JavaDocs: ./gradlew aggregateJavaDoc
  • For the project User Guide: ./gradlew asciidoctor

Overall project documentation may also be generated as the project web site, using the site task, discussed in the next section.

Documentation

The documentation for the project consists of:

  • Web site - landing page and general introduction (src/site directory).
  • User Guide - getting started, examples and detailed usage information (src/docs/asciidoc directory).
  • JavaDocs - unified API documentation (throughout the codebase).
  • Test, Coverage & Quality reports - misc build and quality reports

The documentation is provided by a unified documentation web site, which can be generated using:

./gradlew site

This task uses the com.stehno.gradle.site plugin to aggregate all the documentation sources and generate the project web site. Once it is built, you can verify the generated content by running a local server:

./gradlew startPreview

which will start a preview server (see com.stehno.gradle.webpreview) on a random port copied to your clipboard.

To stop the preview server run:

./gradlew stopPreview`

Once you are ready to publish your site, simply run the following task:

./gradlew publishSite

This task will push the site contents into the gh-pages branch of the project, assuming you have permissions to push content into the repo.

Artifact Release

When ready to release a new version of the project, perform the following steps starting in the development branch:

  1. Ensure that the project version (in build.gradle) has been updated to the desired version.
  2. Run ./gradlew updateVersion -Pfrom=OLD_VERSION to update the documented version.
  3. Create a Pull Request from development to master and accept it or have it reviewed.

Once the pull request has been merged into master, checkout the master branch and:

  1. Run ./gradlew release which will check the documented project version against the project version, publish the artifact and the documentation web site.
    • You will need to provide (or have configured in your HOME/.gradle/gradle.properties file):
      • user - the Bintray username
      • key - the Bintray key/password
      • sonotypeUser - the Sonotype username (from API key)
      • sonotypePass - the Sonotype password (from API key)
    • This step may take some time (on the order of a minute or two depending on server response times).
  2. Manually confirm the publication of the new artifact on the Bintray web site (or the publication will expire) - this step may no longer be needed, but verify anyway.
  3. Run ./gradlew verifyRelease to ensure that the artifacts and site have been published (optional but recommended).
  4. A Git tag should be created for the released version.

The development branch may now be used for the next round of development work.

NOTE: Since the artifacts must be confirmed and the site may need some installation time, the verifyRelease task cannot be combined with the release task.

Version Updates

When updating the version of the project, the documented version should also be updated using the updateVersion task. Modify the version in the project build.gradle file and make note of the existing version then run:

./gradlew updateVersion -Pfrom=OLD_VERSION

where OLD_VERSION is the pre-existing version of the project. This will update the current version mentioned in the documentation (e.g. README, User Guide and site).

History

Http Builder NG was forked from the HTTPBuilder project originally developed by Thomas Nichols. It was later passed on to Jason Gritman who maintained it for several years.

The original intent of Http Builder NG was to fix a few bugs and add a slight enhancement to the original HTTPBuilder project. The slight enhancement was to make HTTPBuilder conform to more modern Groovy DSL designs. However, it was not possible to update the original code to have a more modern typesafe DSL while preserving backwards compatibility. I decided to just make a clean break and give the project a new name to make it clear that Http Builder NG is basically a complete re-write and re-architecture.

License

Copyright 2017 HttpBuilder-Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Original Project

jgritman/httpbuilder

http-builder-ng's People

Contributors

aalmiray avatar adamhurwitz avatar cjstehno avatar dfernandez79 avatar dwclark avatar jageay avatar jagedn avatar jgritman avatar jlafourc avatar kamilszymanski avatar kgeis avatar mbabineau avatar rdmueller avatar sparsick avatar splix avatar spoonman avatar stlhrt avatar szpak avatar wheelerlaw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

http-builder-ng's Issues

Make twitter announce part of release

I created a twitter account for the project HttpBuilderNG - it would be nice to use the announce plugin and publish release notifications to it when the release task is run successfully.

It should verify that the username/password is present or on the command line before announcing.

Implement the old "ignore SSL issues" functionality

In the original HttpBuilder, there was a method ignoreSSLIssues() that would ignore SSL problems - I believe it was made for internal testing and cases when you have a self-signed certificate.

The method itself is pretty simple and not entirely client-specific. The old solution should still work for Apache, but we'd need to find analogous code for OkHttp and the core clients.

Also, there is currently no easy way to test this behavior - there is an issue (Ersatz-8) open for Ersatz to provide HTTPS endpoints.

incorrect generation of URI with multi-valued query parameters

I expect the output to be the same in both of these cases. With UriBuilder, it is different, as you can see.

@Grab('io.github.http-builder-ng:http-builder-ng-core:0.13.3') 
import groovyx.net.http.UriBuilder

def testURL = 'http://test.com/a?b=c&d=e'

println new URI(testURL)
println UriBuilder.basic(UriBuilder.root()).setFull(testURL).toURI()

output:

http://test.com/a?b=c&d=e
http://test.com/a?b=[c]&d=[e]

difficult to handle exceptions

I'm finding it difficult to handle exceptions.

First, I think it is unintuitive that a network error throws an exception and does not call the failure method. I realize that the documentation is clear that .failure(..) is for valid responses with code > 400, but this does not satisfy the principle of least surprise. Note, it's also different from what jQuery.ajax() does.

Also, I'm having trouble catching exceptions because they are wrapped in RuntimeException. I think that it is unnecessary to wrap exceptions like this when the code is intended to be called from Groovy. To illustrate this, I want to ignore SocketException. Instead of writing

} catch (SocketException ex) { }

I need to write

} catch (RuntimeException ex) {
    if (! ex.cause in SocketException) {
        throw ex
    }
}

Multipart Form Support

Add mulitpart form support. Support should be optional and activated if supporting library is present on the classpath (like support for CSV and JSoup). The best library is probably javax.mail 1.5. Different parts should be encoded/parsed using existing parsers and encoders.

Make `publishSite` depend on `site`

There is no reason why you would want to publish a site without making it first, I just need to make sure that it will not re-run the site task even when it has been generated - though, ultimately not as big a deal as accidentally publishing an empty site.

URI query ignored when specified in configure block

I expect these requests to be equivalent, but they are not. The first way does not send query parameters.

@Grab('io.github.http-builder-ng:http-builder-ng-core:0.13.3')
@Grab('org.apache.commons:commons-lang3:3.5')
@Grab('org.jsoup:jsoup:1.10.2')

import groovyx.net.http.HttpBuilder

println HttpBuilder.configure{
    request.uri = 'http://urlecho.appspot.com/echo?status=200&Content-Type=text%2Fhtml&body=Hello%20world!'
}.get()

println HttpBuilder.configure{
    request.uri = 'http://urlecho.appspot.com/echo'
}.get{
    request.uri.query = [status: '200', 'Content-Type': 'text/html', body: 'Hello world!']
}

According to my reading of the documentation, the first way is valid, and it's more straightforward.

Refactor test kits

  • Remove httpbin tests and ensure that their cases are covered in the main test kits
  • Review all tests and test kits to ensure that there is a unified test plan across all functionality and clients
  • consider: sync, async, clients, http, https, BASIC, DIGEST across all or most test cases by verb
  • Ensure active encoder/parser test cases for each verb

OAuth Support

OAuth support should be added back in. I disabled it because I don't understand OAuth and I generally don't want to support a security library I don't understand. The signpost OAuth library is still an optional dependency, but if there is a better library for OAuth let's use that.

Apache client throws exception for BASIC auth in POST

In the HttpPostSpec test, the '[#client] POST (BASIC) /basic: returns content' test fails for the Apache client but passes for the Java client. The exception thrown is:

java.lang.RuntimeException: org.apache.http.client.ClientProtocolException
    at groovyx.net.http.optional.ApacheHttpBuilder.exec(ApacheHttpBuilder.java:292)
    at groovyx.net.http.optional.ApacheHttpBuilder.doPost(ApacheHttpBuilder.java:347)
    at groovyx.net.http.HttpObjectConfigImpl.nullInterceptor(HttpObjectConfigImpl.java:39)
    at groovyx.net.http.HttpBuilder.post(HttpBuilder.java:693)
    at groovyx.net.http.HttpPostSpec.[#client] POST (BASIC) /basic: returns content(HttpPostSpec.groovy:165)
Caused by: org.apache.http.client.ClientProtocolException
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:186)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:71)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:220)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:164)
    at groovyx.net.http.optional.ApacheHttpBuilder.exec(ApacheHttpBuilder.java:289)
    ... 4 more
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity.

which seems related to this SO post (http://stackoverflow.com/questions/8222929/httppost-failed-due-to-cannot-retry-request-with-a-non-repeatable-request-entit) about repeatable entities. Our HttpEntity implementation is "not" repeatable, but I am not sure whether that is intentional or just "because".

Encoders should auto-encode based on request content-type

Similar to how the response content is decoded automatically by content-type, the request body should be encoded using the default internal encoders. You should be able to do something like:

http.post {
    request.uri.path = '/foo'
    request.contentType = 'application/json'
    request.body = [name: 'Bob', age:50]
}

and the request should be converted to JSON.

Not sure whether this is a bug or a missed feature.

Ensure cookies are properly supported

It seems that the Java and Apache client implementations are handling cookies differently; this can be seen in the Issue-marked cookie tests in HttpGetSpec (and others). If the whole class suite is run, the second cookie test executed will contain unexpected cookies in the request (from the first cookie test). This only happens in the Java implementation, not Apache. I have made the cookie-based tests check with contains for now, but this should be investigated.

Its understandable that cookies would be stored across requests for the same client, that's what they do; however, we need to ensure that both clients exhibit the same behavior and that it is configurable if necessary.

While the issue seems to be in the Java client, it would be good to investigate both implementations and document their cookie support better as well as providing any additional configuration hooks.

nttp-builder-ng-core should have explicit dependency on commons-lang3

commons-lang3's BooleanUtils is explicitly called in HttpObjectConfigImpl, but it is not referenced as a project dependency.

When I run a simple script to test http-builder-ng, I get this:

Caught: java.lang.NoClassDefFoundError: org/apache/commons/lang3/BooleanUtils
java.lang.NoClassDefFoundError: org/apache/commons/lang3/BooleanUtils
	at groovyx.net.http.HttpObjectConfigImpl$Exec.<init>(HttpObjectConfigImpl.java:61)
	at groovyx.net.http.HttpObjectConfigImpl.<init>(HttpObjectConfigImpl.java:41)
	at groovyx.net.http.HttpBuilder.configure(HttpBuilder.java:161)
	at groovyx.net.http.HttpBuilder.configure(HttpBuilder.java:137)
	at groovyx.net.http.HttpBuilder$configure.call(Unknown Source)
	at test.run(test.groovy:7)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.lang3.BooleanUtils
	... 6 more

When I add

@Grab('org.apache.commons:commons-lang3:3.5')

to my script, the error goes away.

Better support for parsers and encoders

Let's get some more stability and usability around the parsers and encoders and figure out a clean way to allow defaults without config while still providing a simple means of adding or overriding them.

Add-on: ensure that we have a good set of available parsers and encoders for all standard formats.

Add simpler construction of client impls

Creating the HttpBuilder for different client implementations is a bit overly verbose for common usage (where you just want one of them all the time). We should be able to make the HttpBuilder sub-classes have a cleaner constructor or factory method - the goal being to default the client factory for each implementation.

We have the defaultFactory stuff in there, but I am not sure it really works at this point.

I meant to do this with the split-up, but I forgot.

Refactor testing to reduce duplication

When I pulled the apache client library out into a separate project, I had to split up the tests which caused duplication in the test code. It would be nice to come up with a "test kit" approach to for the HTTP method support tests so that the same tests can easily be run against different client implementations.

Utility methods for Jackson and Csv

Add utility methods to Jackson class and Csv class to make specifying encoders and parsers as easy as it is for Download class.

See Download test method in JavaHttpBuilderTest for usage.

clarify docs: must specify JSoup dependency to get default HTML parser

I tried the following script

@Grab('io.github.http-builder-ng:http-builder-ng-core:0.13.3')
@Grab('org.apache.commons:commons-lang3:3.5')

import groovyx.net.http.HttpBuilder

HttpBuilder.configure{
    request.uri = 'https://google.com/'
}.get{
    response.success{ resp, html ->
        println resp.contentType
        println html.getClass().name
    }
}

I was confused to have the response come back as a byte array. The documentation says:

Default parsers are provided for: HTML (org.jsoup.nodes.Document),...

The way it is worded, I would not expect to need to add JSoup to the classpath in order to parse HTML. When I added JSoup to the classpath, then the response came back as a Document.

Pull out implementation projects

Pull the alternate implementation clients into separate projects:

  • core - the core functionality and common code
  • java - the java-internal client
  • apache - the apache http components client

BASIC and DIGEST support needed?

Does anyone still use BASIC and/or DIGEST for HTTP authentication? Clients seem to implement it differently or in odd ways, or not at all - I have removed the unit tests for DIGEST in the HTTP method test kits for now. We can add them back in where needed/supported, but in general we need to determine whether or not it's worth supporting BASIC and DIGEST at all.

If we decide to support either or both of these going forward we should dig deeper into supporting and testing them across the client implementations.

Please feel free to post comments here if you feel strongly either way or have general suggestions related to this issue.

Add support for proxying connections

We have a request for Http Builder NG to support proxying connections. All of our supported clients have this feature (though of course implemented differently). We should add a proxy property at the request configuration level and then properly use it in the clients.

Java examples

All.the examples are for Groovy but the library should be usable from standard Java - add some documentation and examples of Java use.

Travis build is flaky

Project builds fine locally but fails on travis - figure out why or replace travis with something less "cute".

Wiki Pages With Examples

Need wiki pages showing:

  1. Examples for using http-builder-ng
  2. Description of architecture and a brief description of the main API classes
  3. Description of how to write encoders and parsers
  4. Description of how to integrate a new http client library

JavaDocs For Main API classes.

Scripts no longer pull in transitive dependencies

The following script should work without the explicit commons-lang dependency:

@Grapes([ @Grab('io.github.http-builder-ng:http-builder-ng-core:0.13.2'),
          @Grab('org.apache.commons:commons-lang3:3.5'),
          @Grab('org.jsoup:jsoup:1.9.2') ])
import groovyx.net.http.HttpBuilder
import java.util.concurrent.Executors
import java.util.concurrent.ExecutorService

final int MAX_THREADS = 10
final ExecutorService myPool = Executors.newFixedThreadPool(MAX_THREADS)

def client = HttpBuilder.configure {
    execution.maxThreads = MAX_THREADS;
    execution.executor = myPool;

    request.uri = 'http://www.cnn.com/'
}

def result = client.get()

println(result.select('title').text())

This is most likely due to either the removal of the println statements in build.gradle (not likely) or the explicit additions to the generated pom file during maven central publishing.

Also, this may simply be due to the addition of a method use from that dependency - which would cause it to fail if missing. (most likely)

Release automation

The release from development to master is fairly cut and dried, but it could use some added automation.

  • Publish artifact
  • Build site and publish
  • Verify site and artifacts
  • Setup CI

Could not find artifact io.github.http-builder-ng:http-builder-ng-apache:0.13.2

Hi,

I try to fetch the artifact with the following Gradle configuration:

    dependencies {
        testCompile 'io.github.http-builder-ng:http-builder-ng-apache:0.13.2'
    }

    repositories {
        jcenter()
        mavenLocal()
        mavenCentral()
    }

but get the following error:

Error:Could not find io.github.http-builder-ng:http-builder-ng-apache:0.13.2.
Searched in the following locations:
    https://jcenter.bintray.com/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.pom
    https://jcenter.bintray.com/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.jar
    file:/Users/david/.m2/repository/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.pom
    file:/Users/david/.m2/repository/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.jar
    https://repo1.maven.org/maven2/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.pom
    https://repo1.maven.org/maven2/io/github/http-builder-ng/http-builder-ng-apache/0.13.2/http-builder-ng-apache-0.13.2.jar
Required by:
    project :backend

I also tried older versions and ask colleagues to use the dependency, but they get the same error.

Multipart response parsing

We support multipart response content; however, we don't provide a parser for it. See ParsersSpec.groovy for multipart response content handling. The main reason is that this is not supported by browsers and would only be for APIs - there is little existing library support documented about this. We should be able to bring in JavaMail and use its multipart API to create a parser for multipart response content.

Add site link to Project Page

Add site link (https://dwclark.github.io/http-builder-ng/) to main GitHub page

  1. On the main GitHub page under the menu bar to the right of "The Easy Http Client for Groovy" you should see an "Edit" button
  2. Click the edit button and paste the URL in the web site box and click "Save"

I'd do this, but I don't have permissions to change that kind of stuff.

Support and/or testing of DIGEST

DIGEST support has been a bit flaky, but now Ersatz can actually test it reliably so let's get the DIGEST support working and tested fully.

  • Need to upgrade to Ersatz v0.5.0+
  • Add DIGEST testing to all HTTP methods
  • Ensure that BASIC is also well-tested
  • May need to add extra library to support in OkHttp client

Continuous integration build

It would be nice to have a http://drone.io or Travis build setup to fire when we update master or development.

I can't set this up since I don't have permissions to add webhooks to the project.

Drone is free and easy to setup with your GitHub account.

Coveralls support for multi-project

The multi-project build is only publishing the core project to coveralls. There is additional configuration required to support multi-project builds; however, the example is more complex than I want to deal with right now. Shelving it for later - contributions welcome. :-)

Use type information to simplify Jackson configuration

Force Jackson based requests/responses to use the type safe http verb methods. Jackson already forces type information to be specified, so instead of using specifying it twice, just piggyback on the type safe http verbs.

JavaHttpBuilder writes quoted cookie values

The JavaHttpBuilder is writing cookie values incorrectly (with quotes). I double-checked to make sure it was not my mock server testing causing the issue, but it verifies against a simple request dumper:

Apache
[nio-8080-exec-1] com.example.DemoApplication: Requested Uri: /foo
[nio-8080-exec-1] com.example.DemoApplication: Cookie[biscuit]: wafer

Java
[nio-8080-exec-2] com.example.DemoApplication: Requested Uri: /foo
[nio-8080-exec-2] com.example.DemoApplication: Cookie[biscuit]: "wafer"

Notice the quoted cookie value in the Java version - this should not be quoted. There is an ignored test in HttpGetSpec (on cjs-documentation branch) with a test of this issue. It passes for the Apache builder but fails for the Java builder.

`FromServer.Header.getKeysValues()` results in odd format

The FromServer.Header.getKeysValues() method seems to result in an odd format. See the FromServerSpec.'Header.keysValues' test for an example:

When the header is multi-valued, such as:

keyValue('Forwarded', 'for=192.0.2.60;proto=http;by=203.0.113.43')

The method call results in:

keysValues == [
    Forwarded: [],
    by       : ['203.0.113.43'],
    for      : ['192.0.2.60'],
    proto    : ['http']
] 

which seems incorrect (or non-intuitive). Seems like the it should be a nested map rather than having the original header off by itself.

Need to see how a servlet container handles multi-valued response headers to be sure.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.