Git Product home page Git Product logo

web3j-openapi's Introduction

Web3j Open API

Build Status

Web3j-OpenAPI is a OpenAPI client and server generator from Solidity smart contracts. it provides a way to interact with the Ethereum blockchain via simple and intuitive HTTP requests, abstracting the coding layer. These interactions can be done using :

  • Plain HTTP requests
  • Via the Swagger-UI, which is generated with every project
  • A client application using the webj3-openapi-client implementation

The workflow can be summed in the following steps:

  • Writing a Solidity smart contract
  • Generating the corresponding OpenAPI project using Web3j-OpenAPI
  • Running the generated project as a standalone server
  • Sending HTTP requests using Swagger-UI, client application or Curl request.

Getting started with Web3j-OpenAPI

To generate an OpenAPI project using the Web3j-OpenAPI generator, you need to have the Epirus-CLI installed on your machine (Note - the Epirus CLI has replaced the Web3j CLI). It’s easy to do (for Windows instructions head here):

$ curl -L get.epirus.io | sh

Create a Hello World project

To create a base OpenAPI project using a Hello World contract, run the following :

$ epirus openapi new

You can also generate a Web3j-OpenAPI project using the Web3j-OpenAPI-gradle-plugin.

Configure the project

After having the generated project, you can use the Epirus-CLI to run it using the following command (Note: You will need to create an Epirus Account).

$ epirus login
$ epirus run rinkeby|ropsten

Alternatively, you can configure your application with the following environment variables:

$ export WEB3J_ENDPOINT=<link_to_your_Ethereum_node>
$ export WEB3J_PRIVATE_KEY=<your_private_key>
$ export WEB3J_OPENAPI_HOST=localhost
$ export WEB3J_OPENAPI_PORT=9090

Run the project

If you aren't using the Epirus-CLI, you may run the project using the following Gradle target:

$ cd <project_folder>
$ ./gradlew run

Then, you should be seeing the server logs.

Interact with the project

SwaggerUI

The easiest way to interact with the generated project is via the generated Swagger-UI which can be found on http://<host>:<port>/swagger-ui.

image

Web3j-OpenAPI client

Also, you can use our client implementation via adding the following dependency to your project:

dependencies {
    implementation "org.web3j.openapi:web3j-openapi-client:4.7.1"
}

Then, within the application:

val service = ClientService("http://localhost:9090")
val app = ClientFactory.create(<AppNameApi>::class.java, service)

// Then you have access to all the API resources
val receipt = app.contracts.contractName.deploy()

println("Deployment receipt: ${receipt.contractAddress}")

// ...

For more information, please refer to the documentation.

web3j-openapi's People

Contributors

rach-id avatar iikirilov avatar gtebrean avatar xaviarias avatar alexandrour avatar nicksneo avatar conor10 avatar cleanunicorn avatar

Stargazers

Kaho Li avatar Aditya Arora avatar Giovanni Sanchez avatar  avatar  avatar Enrique Arizón Benito avatar 0xA avatar Nikita Varabei avatar Danny Delott avatar foxgem avatar Leszek Jasek avatar afewnotes avatar  avatar Etch avatar  avatar Alexander avatar  avatar Moncef AOUDIA avatar AboveWallStreet avatar Alvar Laigna avatar  avatar Marut Pandya avatar Ilija avatar lostoy avatar Matt Mertens avatar Kenneth Luster avatar mani avatar James Shih avatar Christian Felde avatar Qdigital avatar outdamud avatar  avatar sam bacha avatar  avatar  avatar

Watchers

James Cloos avatar  avatar  avatar abhi avatar  avatar Andrii avatar  avatar sam bacha avatar  avatar

web3j-openapi's Issues

import from exists contract not generate OpenApi specs

The swagger and openApi specs are empty when I create a new project.

I'm following the documentation and using the command:

web3j openapi import \
    -s=./contracts/contracts/HelloWorld.sol \
    --package=dummy.HelloWorld \
    --project-name=HelloWorld \
    --output-dir=./web3j

First thing I did was added into global gradle settings the missing "consensys" dep to be able to run the project.

when run the task ./gradlew run the swagger doesn't show any helloworld methods showing "no operations defined..."

looking into build/resources/openapi/main/openapi.json I found just the header {"openapi":"3.0.1"}

Looks good the ABIs and metadata files into build/resources/main/solidity...

I'm running a hardhat local node and set the envs:

export WEB3J_ENDPOINT=http://localhost:8545
export WEB3J_PRIVATE_KEY=...
export WEB3J_OPENAPI_HOST=localhost
export WEB3J_OPENAPI_PORT=9090

I'm missing some step?

It's happens too when I just create a new project using default parameters

web3j openapi new

Cannot create openapi from sol8 contracts

Cannot create openapi from sol8 contracts

I cannot use this tool with the contracts created with the new version of solidity.

Description

I'm trying to create the OpenAPI doc from the contracts i've written used OpenZeppelin standards. They use version of solidity compiler greater than 0.7 and when i try to run the command to create the openapi file, it fails since the compiler used is different from the one that contracts use.

Enviroment

Windows 10
Solc 8 compiler (contracts created with this type of solidity compiler)

Solidity function with same name and different case

Problem

We can have the following functionName and FunctionName as two different functions:

{
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "minter",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "indexed": false,
        "internalType": "uint256",
        "name": "amount",
        "type": "uint256"
      }
    ],
    "name": "Mint",
    "type": "event"
  },
{
    "inputs": [
      {
        "internalType": "address",
        "name": "_to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "_amount",
        "type": "uint256"
      }
    ],
    "name": "mint",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  }

Contract sub-resource locator doesn't accept mixed-case addresses

When using a contract sub-resource locator such as:

https://rinkeby.epirus.io/contracts/0x08Ccb6099FcCD8c496d07d0da6f1C85D73D7FD2c

the Jersey server cannot find the resource, since the regex pattern is only accepting lower case hex letters:

const val HEX_40 = "0x[a-f0-9]{40}"

This should be replaced by:

const val HEX_40 = "0x[a-fA-F0-9]{40}"

SwaggerUI not generated at first attempt

In a generated project, when installDist or shadowJar, the SwaggerUI isn't included in the JAR.
To be included, we need to generate the JAR again.
This, I think, comes from the fact that the generation comes after copying the resources of the server containing the SwaggerUI. So, the second attempt we generate, they are taken into consideration.

Incompatible type error when generating uint32 field

When declaring a contract with a public uint32 field, the OpenAPI generated method to retrieve its value is of type Long which makes a compile error.

For example the contract:

contract Test { 
    uint32 test public;
}

generates the following wrapper method:

public RemoteFunctionCall<BigInteger> test() {
    final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_TEST, 
            Arrays.<Type>asList(), 
            Arrays.<TypeReference<?>>asList(new TypeReference<Uint32>() {}));
    return executeRemoteCallSingleValueReturn(function, BigInteger.class);
}

and the OpenAPI resource method:

override fun test(): ResultModel<Long> =
        org.web3j.openapi.core.models.ResultModel(test.test().send())

Cannot initialise deploy parameters field (no delegate- or property-based Creator)

radish34/Registrar : Problem with initializing the address while deploying:

DeployParameters:
data class RegistrarDeployParameters(
  val ERC1820RegistryAddress: String
)
Error:
{
  "title": "Cannot construct instance of `com.gen.app.core.registrar.model.RegistrarDeployParameters` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 2]",
  "responseStatus": 400,
  "requestMethod": "POST",
  "requestUrl": "http://localhost:9090/testApp/contracts/registrar",
  "userAgent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:77.0) Gecko/20100101 Firefox/77.0"
}
How to make it work ?

Add another field to the data class and everything works fine:

data class RegistrarDeployParameters(
  val ERC1820RegistryAddress: String,
  val something: Int
)

In forums, they say to add default constructor, but that doesn't work as the value being sent in the request gets omitted.
Another suggestion is adding the jackson-module-kotlin dependency. However, we already have that.

Edit: The following contracts too: EnumerableMapMock

Project creation fails

epirus new helloworld
Threw an error that the project generation failed and asked to check the logs. However, where the logs can be found
We need more details please about these common issues

Error while sending on SSE event sink

15:14:21.527 [jetty-http-server-1] ERROR o.g.j.server.ServerRuntime$Responder - An I/O error has occurred while writing a response message entity chunk to the container output stream.
org.eclipse.jetty.io.EofException: null
	at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:283)
	at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:422)
	at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:277)
	at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:381)
	at org.eclipse.jetty.server.HttpConnection$SendCallback.process(HttpConnection.java:827)
	at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
	at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:223)
	at org.eclipse.jetty.server.HttpConnection.send(HttpConnection.java:549)
	at org.eclipse.jetty.server.HttpChannel.sendResponse(HttpChannel.java:829)
	at org.eclipse.jetty.server.HttpChannel.write(HttpChannel.java:901)
	at org.eclipse.jetty.server.HttpOutput.channelWrite(HttpOutput.java:283)
	at org.eclipse.jetty.server.HttpOutput.channelWrite(HttpOutput.java:267)
	at org.eclipse.jetty.server.HttpOutput.flush(HttpOutput.java:705)
	at org.glassfish.jersey.message.internal.CommittingOutputStream.flush(CommittingOutputStream.java:263)
	at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:245)
	at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:195)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:219)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:314)
	at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:195)
	at org.glassfish.jersey.server.ChunkedOutput.setContext(ChunkedOutput.java:374)
	at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:687)
	at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:371)
	at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:361)
	at org.glassfish.jersey.server.ServerRuntime$AsyncResponder$3.run(ServerRuntime.java:860)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
	at org.glassfish.jersey.server.ServerRuntime$AsyncResponder.resume(ServerRuntime.java:892)
	at org.glassfish.jersey.server.ServerRuntime$AsyncResponder.resume(ServerRuntime.java:848)
	at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:186)
	at org.glassfish.jersey.server.ChunkedOutput.write(ChunkedOutput.java:181)
	at org.glassfish.jersey.media.sse.internal.JerseyEventSink.send(JerseyEventSink.java:101)
	at org.web3j.openapi.server.SseUtils$subscribe$1.accept(SseUtils.kt:33)
	at io.reactivex.internal.subscribers.LambdaSubscriber.onNext(LambdaSubscriber.java:65)
	at io.reactivex.internal.util.NotificationLite.acceptFull(NotificationLite.java:272)
	at io.reactivex.internal.operators.flowable.FlowableBlockingSubscribe.subscribe(FlowableBlockingSubscribe.java:67)
	at io.reactivex.internal.operators.flowable.FlowableBlockingSubscribe.subscribe(FlowableBlockingSubscribe.java:109)
	at io.reactivex.Flowable.blockingSubscribe(Flowable.java:6031)
	at org.web3j.openapi.server.SseUtils.subscribe(SseUtils.kt:31)
	at com.test.server.humanstandardtoken.ApprovalEventResourceImpl.subscribe(ApprovalEventResourceImpl.kt:45)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory.lambda$static$0(ResourceMethodInvocationHandlerFactory.java:52)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:124)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:167)
	at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$SseEventSinkInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:130)
	at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:469)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:391)
	at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:80)
	at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:253)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:248)
	at org.glassfish.jersey.internal.Errors$1.call(Errors.java:244)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
	at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
	at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
	at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:232)
	at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
	at org.glassfish.jersey.jetty.JettyHttpContainer.handle(JettyHttpContainer.java:173)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
	at org.eclipse.jetty.server.Server.handle(Server.java:500)
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
	at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:135)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.io.IOException: Broken pipe
	at java.base/sun.nio.ch.FileDispatcherImpl.writev0(Native Method)
	at java.base/sun.nio.ch.SocketDispatcher.writev(SocketDispatcher.java:51)
	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:182)
	at java.base/sun.nio.ch.IOUtil.write(IOUtil.java:130)
	at java.base/sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:496)
	at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:263)
	... 81 common frames omitted

Problem with arrays when generating wrappers

I think this is related to Web3J-CodeGen, not sure, might be just the configuration I am calling the generation with.

Problem

The following two contracts:

pragma solidity ^0.4.2;

contract NumArrays {

    function getNum() public pure returns(uint[3] result) {
        return [1, 10, 2**256 - 1];
    }

    function idNum(uint[5] numArr) public pure returns(uint[5] result) {
        return numArr;
    }

}

and

pragma solidity ^0.6.6;

contract Arrays {

    function fixedReverse(uint[10] input) public pure returns(uint[10] result) {
        uint length = input.length;
        for (uint i = 0; i < length; i++) {
            result[i] = input[length - (i + 1)];
        }
        return result;
    }

    function dynamicReverse(uint[] input) public pure returns (uint[] result) {
        uint length = input.length;
        result = new uint[](length);

        for (uint i = 0; i < length; i++) {
            result[i] = input[length - (i + 1)];
        }
        return result;
    }

    function multiDynamic(uint[2][] input) public pure returns (uint[] result) {
        uint length = input.length;
        result = new uint[](length*2);
        uint resultIndex = 0;
        for (uint i = 0; i < length; i++) {
            result[resultIndex] = (input[i][0]);
            resultIndex++;
            result[resultIndex] = (input[i][1]);
            resultIndex++;
        }
        return result;
    }

    function multiFixed(uint[2][6] input) public pure returns (uint[] result) {
        uint length = input.length;
        result = new uint[](length*2);
        uint resultIndex = 0;
        for (uint i = 0; i < length; i++) {
            result[resultIndex] = (input[i][0]);
            resultIndex++;
            result[resultIndex] = (input[i][1]);
            resultIndex++;
        }
        return result;
    }

    function returnArray() public pure returns (address[]) {
        return new address[](0);
    }
}

Fail when generating the wrappers as follows:

Generating com.gen.app.wrappers.Arrays ... java.lang.ClassNotFoundException: org.web3j.abi.datatypes.generated.StaticArray2
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:315)
        at org.web3j.abi.TypeReference.makeTypeReference(TypeReference.java:207)
        at org.web3j.codegen.SolidityFunctionWrapper.buildTypeName(SolidityFunctionWrapper.java:1639)
        at org.web3j.codegen.SolidityFunctionWrapper.buildParameterTypes(SolidityFunctionWrapper.java:1071)
        at org.web3j.codegen.SolidityFunctionWrapper.addParameters(SolidityFunctionWrapper.java:880)
        at org.web3j.codegen.SolidityFunctionWrapper.buildFunctions(SolidityFunctionWrapper.java:1152)
        at org.web3j.codegen.SolidityFunctionWrapper.buildFunctionDefinitions(SolidityFunctionWrapper.java:342)
        at org.web3j.codegen.SolidityFunctionWrapper.generateJavaFiles(SolidityFunctionWrapper.java:216)
        at org.web3j.openapi.codegen.web3jCodegenStuff.SolidityFunctionWrapperGenerator.generate(SolidityFunctionWrapperGenerator.kt:98)
        at org.web3j.openapi.codegen.GenerateOpenApi.generateWrappers(GenerateOpenApi.kt:72)
        at org.web3j.openapi.codegen.GenerateOpenApi.generateAll(GenerateOpenApi.kt:32)
        at org.web3j.openapi.console.GenerateCommand.generate(GenerateCommand.kt:127)
        at org.web3j.openapi.console.GenerateCommand.call(GenerateCommand.kt:106)
        at org.web3j.openapi.console.GenerateCommand.call(GenerateCommand.kt:34)
        at picocli.CommandLine.executeUserObject(CommandLine.java:1853)
        at picocli.CommandLine.access$1100(CommandLine.java:145)
        at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2255)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2249)
        at picocli.CommandLine$RunLast.handle(CommandLine.java:2213)
        at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2080)
        at picocli.CommandLine.execute(CommandLine.java:1978)
        at org.web3j.openapi.console.OpenApiCommand$Companion.main(OpenApiCommand.kt:75)
        at org.web3j.openapi.console.OpenApiCommand.main(OpenApiCommand.kt)

Cut a new release for Web3j-OpenAPI

As per the discussion under #99, the current openapi generated project is not using the latest version of sokt.
Thus, we will need to cut a new release of the library, using the latest sokt and web3j versions, to support the latest solidity versions.

Functions signatures overloading

Problem:

In solidity 0.6.8, not sure if it is the only version that allows this, we can have two functions, with same name, different parameters.

Example:

  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "from",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "to",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "tokenId",
        "type": "uint256"
      },
      {
        "internalType": "bytes",
        "name": "_data",
        "type": "bytes"
      }
    ],
    "name": "safeTransferFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },

Consequences

When generating the OpenAPI, we only take the name of the function and create a data class called: SafeTransferFromParameters. And, which having two overloaded functions, we end up with one declaration overwriting the other.

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.