spectolabs / hoverfly-java Goto Github PK
View Code? Open in Web Editor NEWJava binding for Hoverfly
License: Apache License 2.0
Java binding for Hoverfly
License: Apache License 2.0
Currently Hoverfly-java
only supports to recording modes simulate or capture. The problem is that you use one or other, so for example the first time you run a test to capture the traffic against real service you do HoverflyRule.inCaptureMode
. Then when you don't want to touch the real traffic you need to do HoverflyRule.inSimulateMode
. So as you can see you need to effectively modify the test to adapt the test lifecycle, first run by instantiating Hoverfly in capture and next times for simulate mode.
To avoid this change an aggregation of both modes so you can do something like:
HoverflyRule.inDualMode(SimulationSource, HoverflyConfig, Type)
where Type
can be:
inSimulate
inCapture
simulate
), if not as WRITE (capture
).I can provide a PR about this.
hoverfly supports using custom SSL certificate and key via the following command:
hoverfly start -key ca.key -cert ca.crt
We should enable this feature in hoverfly-java, so that when we do:
@ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(
dsl(server("https://test.com").get("/").willReturn(success())),
configs()
.sslCertificatePath("/path/to/crt/file")
.sslKeyPath("/path/to/key/file"));
Just a few lines of code, we have an HTTPS server endpoint for testing SSL clients. It is much better than configuring my own HTTPS server using the existing JAVA libraries.
I'm facing below error while using Java as middleware.
Hoverfly could not execute this middleware
Following is the command I'm trying to start.
hoverctl middleware --binary java --script C:\tmp\Test.class
Is Java as middleware supported? Am I using the command incorrectly? or any issue with Java version?
When calling hoverfly.start
a second time (without stopping it), you get a port already in use
error, as opposed to something more helpful such as Hoverfly is already running
, or even doing nothing at all.
I'm not sure which of the two options would be best. Does anyone have any suggestions?
Hoverfly returns a 503
when it can't find a matching response for a request. The application code will then receive this, and try to handle it. In reality it would make more sense for the library to throw something like a NoMatchFoundException
on a 503
which is much clearer feedback.
Would be nice to be able to set the log level of Hoverfly using HoverflyConfig
, for example:
configs().logLevel(ERROR);
The current implementation of Hoverfly startup process creates custom trust store with Hoverfly's self-signed certificate, and set it in the default SSLContext
, making Hoverfly the only trusted HTTPS server.
If an HTTP client happens to use default SSLContext
, and makes request to third party HTTPS server, the request will fail with javax.net.ssl.SSLHandshakeException
http://todobackend.com/ has a patch verb usage.
Would be cool if hoverfly-java did too (hoverfly can capture patch, and simulate it standalone)
Starting from v0.5.0, the simulation scheme allows matchers in the request data, we should make use of it from the DSL.
There are a few changes I am proposing:
service("www.test.com") // match on any http protocol
service("http://www.test.com") // match exactly on scheme=http, and destination=www.test.com
service(any()) // match on any scheme and any destination
service("www.test.co*") // enable glob matcher, that will match any destination begins with 'www.test.co'
// or maybe a bit Java-ish:
service(startsWith("www.test.co")) // same as "www.test.co*"
service(endsWith("test.com")) // same as "*test.com"
service(contains("test")) // same as "*test*"
service(matches("www.*-test.com")) // explicitly set glob matching
// then later on when we support regex matcher
service(matchesRegex("www.[test|fail].com"))
service("www.test.com")
.anyMethod("/api/foo") // match any HTTP methods made to '/api/foo' endpoint
.get("/api/v1/foo") // match '/api/v1/foo' exactly
.get(any()) // match GET request to any url path
.get("/api/*/foo") // match paths such as '/api/v1/foo', '/api/v2/foo', behind the scene, it uses glob matcher
// or
.get(matches("/api/*/foo"))
.get("/api/v1/foo")
.queryParam(any()) // match request with any params
.queryParam(any(), "foo") // match any query has value "foo"
.queryParam("foo", any()) // match any query has key "foo" with any value
// JSON
.body(matchesJsonPath("<jsonpath expression>"))
.body(equalsToJson("<request body in JSON>"))
.body(equalsToJson(obj)) // will serialize the object to JSON
// XML
.body(matchesXmlPath("<xmlpath expression>"))
.body(equalsToXml("<request body in XML>"))
.body(equalsToXml(obj)) // will serialize the object to XML
// Others
.body(any()) // match request with any body, this will include empty or null body
.body(matchesJson().arrayNode("items").get(0).getKey("price").equalsTo(100));
maybe it is too much.
If .body()
or .queryParams()
is absent, we are assuming that the user want to match request that has no body or query params
Glob matcher can be built using these methods:
any()
-> *
startsWith("foo")
-> foo*
endsWith("foo")
-> *foo
contains("foo")
-> *foo*
matches("fo*o")
-> fo*o
The ability to verify requests which have been made would be useful, in a way similar to Mockito but for http. The problem stems around us being dependant on the functionality to be implemented in Hoverfly itself, although it is currently on the roadmap.
Create module for JUnit 5 support, so Hoverfly can be used as Rule or JUnit 5 Module.
Currently working on that. PR coming in next days/week
It would be beneficial to add documentation to readthedocs
HoverflyConfig provides an option to use a remote hoverfly instance to simulate or capture API simulations.
The following set system properties have been hard-coded though:
System.setProperty("http.proxyHost", "localhost");
System.setProperty("https.proxyHost", "localhost");
This is a potential bug that should be looked into.
This library does not respect these system properties by default
It is nice to build Hoverfly simulation with the DSL. But when things go wrong, I will need to pause the test and call the hoverfly admin endpoint to inspect the simulation data.
There should be an option to pretty print the simulation data in the console.
Currently to return a success you need to skip double quotes:
.willReturn(success("{\"bookingId\":\"1\"}", "application/json"))
With small documents is ok but if it is bigger might annoying dealing with it. There are some workarounds like reading from file, but providing a method that allows you to use single quotes as double quotes might simplify things, so for example previous example could be written:
.willReturn(successWithSingleQuotes("{'bookingId':'1'}", "application/json"))
Which is quite better to read but also to write.
I'll provide a PR.
Hoverfly no longer capture request headers by default since the introduction of the v2 schema, see [Release v0.11.0] (https://github.com/SpectoLabs/hoverfly/releases/tag/v0.11.0)
I am proposing to introduce this config options:
// Capture all headers
configs().captureHeaders();
// Capture a list of headers
configs().captureHeaders("Authorization", "Origin");
This option will only take effect when Hoverfly is in capture mode.
When connecting to remote hoverfly instance, it is likely that the authentication is enabled:
https://docs.hoverfly.io/en/stable/pages/tutorials/advanced/proxyauth/proxyauth.html#proxyauth
The remote hoverfly instance config should allow user to provide username and password for the basic authentication of the hoverfly proxy.
Currently our internal Jenkins tests and builds the project, but we would benefit more from GitHub CI integration
If proxyLocalHost is set to true in HoverflyConfig, http.nonProxyHosts will be set to "" in the Hoverfly class.
If I then try to add a simulation source to the hoverfly instance, the rest call towards the admin port will be intercepted by the hoverfly proxy.
This, for example, will fail immediately:
HoverflyRule.inSimulationMode(classpath("simulation.json"), HoverflyConfig.configs().proxyLocalHost(true));
with:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:37292/api/v2/simulation returned a response status of 502 Bad Gateway
Because it goes through the hoverfly proxy when trying to reach the admin port.
Instead of setting nonProxyHosts to "", an exception should be added for the admin port.
It would be good to also be able to define ports that are exceptions via HoverflyConfig.
Generating a list of dependent services after JUnitRule finishes recording data would be useful. Tools like Specto could be used to load the dependencies.yaml
to visualize a graph.
During capture mode, the simulation JSON which is written to the filesystem appears on one line instead of being formatted properly.
Sometimes you may not want to import service data, and instead might want to be able to create it programatically. This could be done by a DSL which lets you build expected requests and responses.
Given the following HoverflyRule:
@Rule
public HoverflyRule hoverflyRule = HoverflyRule.inCaptureMode("recorded-simulation.json");
The recorded json file will be saved in the test/resources/
folder. There is a risk of overriding existing file if it has the same name.
It'd be better to save recorded json file into a root directory such as test/resources/hoverfly/
.
The guava dependency is only used in a few places, and it doesn't seem worth exporting transitively for the little benefit it's providing right now.
I would recommend removing it for now, It can always be reintroduced later if it becomes necessary.
Hoverfly supports service latency in simulation:
http://hoverfly.io/usage/#simulating-service-latency
This would be useful in HoverflyRule, so that global delay settings can be configured, and also individual delay can be added to response when using DSL.
An issue reported by Bas Dijkstra:
I keep getting an error message when hoverfly tries to delete the
temporary binary. It has created the stub successfully though (tested that
with a REST Assured test).
Any thoughts? Here's the error stack trace.
java.lang.IllegalStateException: Failed to delete hoverfly binary
at io.specto.hoverfly.junit.core.Hoverfly.stop(Hoverfly.java:163)
at io.specto.hoverfly.junit.rule.HoverflyRule.after(HoverflyRule.java:183)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at
org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.nio.file.AccessDeniedException:
C:\Users\Bas\AppData\Local\Temp\hoverfly_windows_amd64.exe132150163173896543
at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
at sun.nio.fs.WindowsFileSystemProvider.implDelete(Unknown Source)
at sun.nio.fs.AbstractFileSystemProvider.deleteIfExists(Unknown Source)
at java.nio.file.Files.deleteIfExists(Unknown Source)
at io.specto.hoverfly.junit.core.Hoverfly.stop(Hoverfly.java:161)
... 10 more
The current implementation will ignore Hoverfly mode configs for remote Hoverfly instance, meaning that it cannot be switched to capture mode.
If the HoverflyRule DSL can automatically marshal/ unmarshal request/response content based on Content-Type
header, that will be a massive advantage. So instead of
.willReturn(response()
.status(200)
.body("{\"bookingId\":\"1\",\"origin\":\"London\",\"destination\":\"Singapore\",\"time\":\"2011-09-01T12:30\",\"_links\":{\"self\":{\"href\":\"http://localhost/api/bookings/1\"}}}")
.header("Content-Type", "application/json")
It could be simplified to this:
.willReturn(response()
.status(200)
.body(bookingA)
.header("Content-Type", "application/json")
where bookingA is a Booking
object
When running some tests using Hoverfly Java where some other JAXRS provider (like Resteasy in Wildfly Swarm) in classpath you get next exception:
java.lang.ExceptionInInitializerError
at com.sun.jersey.core.spi.factory.MessageBodyFactory.initReaders(MessageBodyFactory.java:182)
at com.sun.jersey.core.spi.factory.MessageBodyFactory.initReaders(MessageBodyFactory.java:176)
at com.sun.jersey.core.spi.factory.MessageBodyFactory.init(MessageBodyFactory.java:162)
at com.sun.jersey.api.client.Client.init(Client.java:343)
at com.sun.jersey.api.client.Client.access$000(Client.java:119)
at com.sun.jersey.api.client.Client$1.f(Client.java:192)
at com.sun.jersey.api.client.Client$1.f(Client.java:188)
at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
at com.sun.jersey.api.client.Client.<init>(Client.java:188)
at com.sun.jersey.api.client.Client.<init>(Client.java:160)
at com.sun.jersey.api.client.Client.create(Client.java:673)
at io.specto.hoverfly.junit.core.Hoverfly.<init>(Hoverfly.java:73)
at io.specto.hoverfly.junit.rule.HoverflyRule.<init>(HoverflyRule.java:93)
at io.specto.hoverfly.junit.rule.HoverflyRule.inCaptureMode(HoverflyRule.java:142)
at io.specto.hoverfly.junit.rule.HoverflyRule.inCaptureMode(HoverflyRule.java:131)
at org.music.ServiceVirtualizationTest.<clinit>(ServiceVirtualizationTest.java:16)
at sun.misc.Unsafe.ensureClassInitialized(Native Method)
at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:142)
at java.lang.reflect.Field.acquireFieldAccessor(Field.java:1088)
at java.lang.reflect.Field.getFieldAccessor(Field.java:1069)
at java.lang.reflect.Field.get(Field.java:393)
at org.junit.runners.model.FrameworkField.get(FrameworkField.java:73)
at org.junit.runners.model.TestClass.getAnnotatedFieldValues(TestClass.java:230)
at org.junit.runners.ParentRunner.classRules(ParentRunner.java:255)
at org.junit.runners.ParentRunner.withClassRules(ParentRunner.java:244)
at org.junit.runners.ParentRunner.classBlock(ParentRunner.java:194)
at org.junit.runners.ParentRunner.run(ParentRunner.java:362)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: org.jboss.resteasy.spi.ResteasyProviderFactory
at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:152)
at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:120)
at javax.ws.rs.core.MediaType.valueOf(MediaType.java:179)
at com.sun.jersey.core.header.MediaTypes.<clinit>(MediaTypes.java:65)
... 38 more
Caused by: java.lang.ClassNotFoundException: org.jboss.resteasy.spi.ResteasyProviderFactory
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:114)
at javax.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:207)
at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:135)
... 41 more
This class of JAXRS providers might happen in other scenarios where for example tests are executed within the container. I suggest ( I can help on that) on changing client from JAXRS to one more neutral implementation like OkHttp + gson/jackson
See title, it looks like Hoverfly does not automatically generate files in subdirectories. For example:
ava.io.FileNotFoundException: src/test/resources/hoverfly/child-directory/should-create-deployment-create-service.json (No such file or directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:270)
at java.io.FileOutputStream.<init>(FileOutputStream.java:213)
at java.io.FileOutputStream.<init>(FileOutputStream.java:162)
at com.fasterxml.jackson.core.JsonFactory.createGenerator(JsonFactory.java:1151)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:971)
at io.specto.hoverfly.junit.core.Hoverfly.persistSimulation(Hoverfly.java:239)
at io.specto.hoverfly.junit.core.Hoverfly.exportSimulation(Hoverfly.java:231)
at io.specto.hoverfly.junit.rule.HoverflyRule.after(HoverflyRule.java:218)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:50)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Even worse if this happens it in a test, the test still passes as opposed to failing.
I am working on a project which includes Hoverfly tool.I have problem to connect the tool with Java Application . The purpose to do that is because i need to use the methods capture and simulate through the Java Application. I will be gratefull if you are going to give me more specific example, how can i do that. In the example on the official site there is some code example but when it doesn't work for me.
Best regards.
Hoverfly Client is used for communicating with hoverfly's Rest API, therefore it should be configured to ignore the JVM proxy settings. All requests from the HoverflyClient should never be processed by hoverfly.
When Hoverfly is already running and there is a port clash, the process fails to start but the rule reports Hoverfly as healthy. This is because it performs the health check on the previous process, instead of the failed one, which reports itself as healthy as it is a Hoverfly instance.
Hi,
I have a situation where I need to stub SOAP API using hoverfly. I have some data fields in input which should come as it is in output. How can I read the input field from json file and put that in output by creating stub using hoverfly? As of now I get static response for any request. I need response as per mu request (for example lets say if my request contains one of the tag as 10then my response should also contains 10. this input amount changes for different users and this should change in response as well while replaying request
HoverflyRule by default starts up Hoverfly and set it as JVM default proxy. This will block HTTP traffic to those non-registered remote APIs. Tests that require both stub and real services will not function.
HoverflyRule should probably support the Hoverfly destination filtering to control what it captures or simulates:
https://docs.hoverfly.io/en/latest/pages/keyconcepts/destinationfiltering.html
Sometimes it may be beneficial to be able to point to a remote Hoverfly, rather than managing the process yourself.
Currently, you are stuck only being able to capture to the file declared within the rule.
hoverfly.capture("path")
should be able to be called as many times as you want, meaning that a new recording to a new destination will start each time it's called. This makes it easy to update the data being recorded for multiple tests at one.
Issue
When running HoverflyRule
in capture mode as a @Rule
against an HTTPS website I am getting an sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
exception.
However if I run the same rule as a @ClassRule
it works and if I run the tests in sequence so that the @ClassRule
test runs before the @Rule
one it also works.
Logs
For the first scenario where only the @Rule
test is run and it fails checked in the attached archive: docs/running-rule-test-alone.log
For the third scenario where the @Rule
is run in sequence after the @ClassRule
test check docs/running-rule-test-after-classrule-test.log
Reproduction
In the attached maven project:
mvn test -Dtest=HoverflyEvaluatorRuleTest
to reproduce the failuremvn test
to reproduce the third scenario aboveOS OSX El Capitan 10.11.6 (15G1108)
HW MacBook Pro (Retina, 15-inch, Mid 2015)
Versions
Attachments
hoverflytest.tar.gz
Hoverfly-java currently redirects hoverfly stdout logs (which is in JSON format) to Java logger. This will format each log entry in JSON format into one liner. It is not very readable when it contains crucial debug info such as closest matches.
Hoverfly-java modifies the default JVM proxy host and port system properties. These values do not get reset when hoverfly shut down. If another test relies on the default values, this modification could cause test failure.
When you run it standalone, you have to do hoverfly.importSimulation(empty())
to clear it. This seems a bit messy, and something along the lines of hoverfly.deleteSimulation()
would be more expressive.
A try with resources way of guaranteeing Hoverfly is terminated would be useful, along with other mechanisms to guarantee it's terminated with the JVM such as automatically registering a shutdownhook to close it.
The existing healthcheck is based on the stats endpoint: /api/stats
which is not intended for returning the status of a running Hoverfly instance.
We should use the /api/health
endpoint instead.
HoverflyRule should always start Hoverfly from a clean state, meaning that there is no existing simulation or journal.
The following commands should also trigger a full reset:
hoverflyRule.capture('test.json')
hoverflyRule.simulate(classPath('test.json))
In addition, HoverflyRule
should provide manual reset methods:
// delete all simulations and journal logs
hoverflyRule.reset();
// delete journal logs but reset simulation to the original SimulationSource
hoverflyRule.resetToDefault();
I am running two consecutive tests with different JSON. The second test fails unless I make it wait a couple of seconds for Hoverfly to complete exiting from the previous test.
I might be mocking my service endpoints, but wanting my HTML, JS, CSS, PNG to be statically served.
service(domain).unprocessed(myUnProcessedHandler)
UnProcessedHandler would have a method:
Response handleUnprocessed(Request req) {
}
You should be able to fix response codes, mimetypes, set bodies in there.
Also, a Response.DO_ACTUAL_PROXY_HTTP_CALL would be nice (for my HTML etc need).
The builder
for rule could be modified so you can pass custom middleware into Hoverfly
When HoverflyRule is annotated with @Rule
, the exportSimulation
is called after each test, meaning that, it will keep overwriting old captured data because of the same filename.
The solution could be postfixing the file name with the test class names, so that
@Rule
public HoverflyRule hoverflyRule = HoverflyRule.inCaptureMode("booking_service_test.json")
@Test
public shouldGetBooking() {
}
@Test
public shouldDeleteBooking() {
}
should create two files under test/resources/hoverfly
booking_service_test_shouldGetBooking.json
booking_service_test_shouldDeleteBooking.json
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.