Git Product home page Git Product logo

openwhisk-action-builder's Introduction

Openwhisk Action Builder

Library and Commandline tool support for building and deploying OpenWhisk actions.

Note: This package is deprecated and will no longer be worked on. Helix-Deploy is the direct successor (100% CLI-compatible) and allows to build and deploy actions to multiple clouds.

Status

GitHub license GitHub issues CircleCI codecov LGTM Code Quality Grade: JavaScript

Setup

  1. Add this wrapper as dev dependency:

    # Add OpenWhisk wrapper as dependency 
    npm add openwhisk-action-builder
  2. add a build script to your package.json:

    "scripts": {
      "build": "./node_modules/.bin/wsk-builder"
    }
  3. Build the OpenWhisk action

    $ npm run build
    ...
    Created action: dist/my-example.zip.
  4. Deploy the OpenWhisk action

    $ wsk action update ....

The deploy parameters can be specifies in the CLI via -p. See below.

CLI

The command line interface wsk-builder can either be invoked via ./node_modules/.bin/wsk-builder. you can also use npx: npx wsk-builder or install it globally npm install -g openwhisk-action-builder.

$ wsk-builder --help
Operation Options
  --build              Build the deployment package    [boolean] [default: true]
  --deploy             Automatically deploy to OpenWhisk
                                                      [boolean] [default: false]
  --test               Invoke action after deployment. Can be relative url.
                                                                        [string]
  --test-params        Invoke openwhisk action after deployment with the given
                       params.                             [array] [default: []]
  --hints, --no-hints  Show additional hints for deployment
                                                       [boolean] [default: true]
  --update-package     Create or update wsk package.  [boolean] [default: false]
  --version-link, -l   Create symlinks (sequences) after deployment. "major" and
                       "minor" will create respective version links      [array]
  --linkPackage        Package name for version links                   [string]

OpenWhisk Action Options
  --name             OpenWhisk action name. Can be prefixed with package.
  --kind             Specifies the action kind.           [default: "nodejs:10"]
  --docker           Specifies a docker image.
  --params, -p       Include the given action param. can be json or env.
                                                           [array] [default: []]
  --params-file, -f  Include the given action param from a file; can be json or
                     env.                                  [array] [default: []]
  --web-export       Annotates the action as web-action[boolean] [default: true]
  --raw-http         Annotates the action as raw web-action (enforces
                     web-export=true)                 [boolean] [default: false]
  --web-secure       Annotates the action with require-whisk-auth. leave empty
                     to generate random token.                          [string]
  --timeout, -t      the timeout limit in milliseconds after which the action is
                     terminated                                 [default: 60000]

OpenWhisk Package Options
  --package.name         OpenWhisk package name.                        [string]
  --package.params       OpenWhisk package params.         [array] [default: []]
  --package.params-file  OpenWhisk package params file.    [array] [default: []]
  --package.shared       OpenWhisk package scope.     [boolean] [default: false]

Bundling Options
  --static, -s  Includes a static file into the archive    [array] [default: []]
  --entryFile   Specifies the entry file.              [default: "src/index.js"]
  --externals   Defines the externals for webpack.         [array] [default: []]

Options:
  --version      Show version number                                   [boolean]
  --verbose, -v                                       [boolean] [default: false]
  --directory    Project directory                       [string] [default: "."]
  --namespace    OpenWhisk namespace. Needs to match the namespace provided with
                 the openwhisk credentials.
  --pkgVersion   Version use in the embedded package.json.
  --modules, -m  Include a node_module as is.              [array] [default: []]
  --help         Show help                                             [boolean]

With no arguments,the wsk-builder just bundles your code into the respective action.zip:

Automatically deploy to openwhisk

When given the --deploy, the wskbot will try to deploy it ot OpenWhisk using the settings from ~/.wskprops. Alternatively, you can also set the WSK_NAMESPACE, WSK_AUTH, WSK_APIHOST in your environment or .env file.

$ wsk-builder --deploy --no-hints
ok: created action: dist/my-example.zip.
ok: updated action tripod/my-example

Automatically test the deployed action

In order to quickly test the deployed action, wsk-builder can send a GET request to the action url.

$ wsk-builder --deploy --no-hints --test
ok: created action: dist/my-example.zip.
ok: updated action tripod/my-example
--: requesting: https://runtime.adobe.io/api/v1/web/tripod/default/my-example ...
ok: 200

the --test argument can be a relative url, in case the request should not be made against the root url, eg:

$ wsk-builder --deploy --no-hints --test=/ping
ok: created action: dist/my-example.zip.
ok: updated action tripod/my-example
--: requesting: https://runtime.adobe.io/api/v1/web/tripod/default/my-example/ping ...
ok: 200

Including action parameters

Action parameters can be defined via -p, either as json on env string, or json or env file.

Examples:

# specify as env string
wsk-builder -p MY_TOKEN=1234 -p MY_PWD=foo

# specify as json string
wsk-builder -p '{ "MY_TOKEN": 1234, "MY_PWD": "foo" }'

# specify as env file
wsk-builder -f .env

# specify as json file
wsk-builder -f params.json

# and a combination of the above
wsk-builder -f .env -f params.json -p MY_TOKEN=123

# like in curl, you can include file contents with `@` (also works in .env or .json file)
wsk-builder -p [email protected]

Specifying arguments in the package.json

Instead of passing all the arguments via command line, you can also specify them in the package.json in the wsk object. eg:

{
...
  "scripts": {
    "build": "./node_modules/.bin/wsk-builder -v",
    "deploy": "./node_modules/.bin/wsk-builder -v --deploy --test"
  },
  "wsk": {
    "name": "my-test-action",
    "params-file": [
      "secrets/secrets.env"
    ],
    "externals": [
      "fs-extra",
      "js-yaml",
      "dotenv",
      "bunyan",
      "bunyan-loggly",
      "bunyan-syslog",
      "bunyan-format"
    ],
    "docker": "adobe/probot-ow-nodejs8:latest"
  },
...
}

Versioning your action

It can be helpful to version the action name, eg with the @version notation. So for example

"wsk": {
  "name": "[email protected]"
}

In order to automatically use the version of the package.json use:

"wsk": {
  "name": "my-action@${version}"
}

Note: the version is internally taken from the pkgVersion variable, so it can be overridden with the --pkgVersion argument, in case it should be deployed differently.

Automatically create semantic versioning sequence actions

By using the --version-link (-l), the bulider can create action sequences linking to the deployed version, using the semantic versioning notation: latest, major, minor:

Action Name Specifier Sequence Name
[email protected] latest foo@latest
[email protected] major foo@v2
[email protected] minor [email protected]

Including static files

Adding static files, i.e. files that are not referenced from the index.js and detected by webpack, can be done via the -s parameter. they are always put into the root directory of the archive.

Example:

# include an image
wsk-builder -s logo.png

If the path points to a directory, it is recursively included.

The files of static files can also be specified in the package.json which allows specifying the destination filename. eg:

...
  "wsk": {
    ...
    "static": [
      "config.json",
      ["assets/logo.png", "static/icon.ong"],
      ["public/", "static/"],
    ]
  }
...

Using the development server

Testing an openwhisk action that was expressified using ActionUtils.expressify() can be done with the DevelopmentServer. Just create a test/dev.js file with:

const { DevelopmentServer } = require('@adobe/openwhisk-action-builder');
const App = require('../src/app.js');

async function run() {
  const devServer = await new DevelopmentServer(App).init();
  return devServer.start();
}

// eslint-disable-next-line no-console
run().catch(console.error);

and run node test/dev.js.

Using development params with the server

Sometimes it might be useful to specify action params that would be provided during deployment but are not available during development. those can be specified by a dev-params-file wsk property. those parameters are loaded an applied to every action call. eg:

...
  "wsk": {
    ...
    "dev-params-file": ".dev-secrets.env"
  }
...

Notes

Bundling

The action is created using webpack to create bundle for the sources and then creates a zip archive with the bundle, a package.json, the private key files and the .env.

Contributing

If you have suggestions for how these OpenWhisk Action Utilities could be improved, or want to report a bug, open an issue! We'd love all and any contributions.

For more, check out the Contributing Guide.

openwhisk-action-builder's People

Contributors

dependabot[bot] avatar dominique-pfister avatar greenkeeper[bot] avatar kptdobe avatar renovate-bot avatar renovate[bot] avatar semantic-release-bot avatar trieloff avatar tripodsan avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 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

openwhisk-action-builder's Issues

An in-range update of yargs is breaking the build 🚨

The dependency yargs was updated from 13.2.2 to 13.2.4.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

yargs is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • ❌ build: * build - Failed

Commits

The new version differs by 14 commits.

  • a6e67f1 chore(release): 13.2.4
  • fc13476 chore: update standard-verison dependency
  • bf46813 fix(i18n): rename unclear 'implication failed' to 'missing dependent arguments' (#1317)
  • a3a5d05 docs: fix a broken link to MS Terminology Search (#1341)
  • b4f8018 build: add .versionrc that hides test/build
  • 0c39183 chore(release): 13.2.3
  • 08e0746 chore: update deps (#1340)
  • 843e939 docs: make --no- boolean prefix easier to find in the docs (#1338)
  • 84cac07 docs: restore removed changelog of v13.2.0 (#1337)
  • b20db65 fix(deps): upgrade cliui for compatibility with latest chalk. (#1330)
  • c294d1b test: accept differently formatted output (#1327)
  • ac3f10c chore: move .hbs templates into .js to facilitate webpacking (#1320)
  • 0295132 fix: address issues with dutch translation (#1316)
  • 9f2468e doc: clarify parserConfiguration object structure (#1309)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

ignore bundle validation for --no-build

> wsk-builder --no-build -no-hints -l latest -l major -l minor

openwhisk-action-builder v2.3.0
--: validating bundle ...
error: { Error: Cannot find module '/root/repo/dist/helix-services-private/[email protected]'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)
    at Function.Module._load (internal/modules/cjs/loader.js:508:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at ActionBuilder.validateBundle (/root/repo/node_modules/@adobe/openwhisk-action-builder/src/action_builder.js:517:16)
    at ActionBuilder.run (/root/repo/node_modules/@adobe/openwhisk-action-builder/src/action_builder.js:773:16) code: 'MODULE_NOT_FOUND' }
error: Validation failed: Error: Cannot find module '/root/repo/dist/helix-services-private/[email protected]'

expressify should emulate raw data

the expressify helper adapts the req.body buffer from serverless-http to a string. it would be better to just emit raw data again, so that the underlying express app can deal with it accordingly.

deploy --hints shows wrong output when deploying non web action

deploy shows suggestion for curl test, instead of wsk action invoke.

for example:

wsk-builder -v --deploy --timeout 300000 --web-export false --test

openwhisk-action-builder v2.2.1
ok: created action: dist/services/[email protected].
Deploying dist/services/[email protected] as services/[email protected] to OpenWhisk
ok: updated action /helix-index/services/[email protected]

You can verify the action with:
$ curl "https://adobeioruntime.net/api/v1/web/helix-index/services/[email protected]"
--: invoking: services/[email protected] ...
ok: 200
{
  "body": "Required arguments missing",
  "statusCode": 400
}

but it should show something like

...
You can verify the action with:
$ wsk action invoke -r services/[email protected]

allow to create/update wsk package

add support for --package which will create and/or update the wsk package.

--package
--package.name
--package.shared
--package.params
--package.params-file

this can also be placed into the wsk.package object in the package.json

Add support for nested action and package params

it should be possible to specify nested package and action params.

eg:

 "wsk": {
    "name": "test",
    "package": {
      "name": "params-test",
      "params": {
        "SECRETS": {
          "MY_TOKEN": "abc",
          "SOME_PWD": "123"
        }
      }
    }
  },

or similar in a json file.

An in-range update of mocha is breaking the build 🚨

The devDependency mocha was updated from 6.0.2 to 6.1.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

mocha is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • ❌ build: * build - Failed

Release Notes for v6.1.0

6.1.0 / 2019-04-07

πŸ”’ Security Fixes

  • #3845: Update dependency "js-yaml" to v3.13.0 per npm security advisory (@plroebuck)

πŸŽ‰ Enhancements

  • #3766: Make reporter constructor support optional options parameter (@plroebuck)
  • #3760: Add support for config files with .jsonc extension (@sstephant)

πŸ“  Deprecations

These are soft-deprecated, and will emit a warning upon use. Support will be removed in (likely) the next major version of Mocha:

πŸ› Fixes

  • #3829: Use cwd-relative pathname to load config file (@plroebuck)
  • #3745: Fix async calls of this.skip() in "before each" hooks (@juergba)
  • #3669: Enable --allow-uncaught for uncaught exceptions thrown inside hooks (@givanse)

and some regressions:

πŸ“– Documentation

πŸ”© Other

  • #3830: Replace dependency "findup-sync" with "find-up" for faster startup (@cspotcode)
  • #3799: Update devDependencies to fix many npm vulnerabilities (@XhmikosR)
Commits

The new version differs by 28 commits.

  • f4fc95a Release v6.1.0
  • bd29dbd update CHANGELOG for v6.1.0 [ci skip]
  • aaf2b72 Use cwd-relative pathname to load config file (#3829)
  • b079d24 upgrade deps as per npm audit fix; closes #3854
  • e87c689 Deprecate this.skip() for "after all" hooks (#3719)
  • 81cfa90 Copy Suite property "root" when cloning; closes #3847 (#3848)
  • 8aa2fc4 Fix issue 3714, hide pound icon showing on hover header on docs page (#3850)
  • 586bf78 Update JS-YAML to address security issue (#3845)
  • d1024a3 Update doc examples "tests.html" (#3811)
  • 1d570e0 Delete "/docs/example/chai.js"
  • ade8b90 runner.js: "self.test" undefined in Browser (#3835)
  • 0098147 Replace findup-sync with find-up for faster startup (#3830)
  • d5ba121 Remove "package" flag from sample config file because it can only be passes as CLI arg (#3793)
  • a3089ad update package-lock
  • 75430ec Upgrade yargs-parser dependency to avoid loading 2 copies of yargs

There are 28 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

An in-range update of webpack is breaking the build 🚨

The dependency webpack was updated from 4.35.3 to 4.36.0.

🚨 View failing branch.

This version is covered by your current version range and after updating it in your project the build failed.

webpack is a direct dependency of this project, and it is very likely causing it to break. If other packages depend on yours, this update is probably also breaking those in turn.

Status Details
  • ❌ build: * build - Failed

Release Notes for v4.36.0

Features

  • SourceMapDevToolPlugin append option now supports the default placeholders in addition to [url]
  • Arrays in resolve and parser options (Rule and Loader API) support backreferences with "..." when overriding options.
Commits

The new version differs by 42 commits.

  • 95d21bb 4.36.0
  • aa1216c Merge pull request #9422 from webpack/feature/dot-dot-dot-merge
  • b3ec775 improve merging of resolve and parsing options
  • 53a5ae2 Merge pull request #9419 from vankop/remove-valid-jsdoc-rule
  • ab75240 Merge pull request #9413 from webpack/dependabot/npm_and_yarn/ajv-6.10.2
  • 0bdabf4 Merge pull request #9418 from webpack/dependabot/npm_and_yarn/eslint-plugin-jsdoc-15.5.2
  • f207cdc remove valid jsdoc rule in favour of eslint-plugin-jsdoc
  • 31333a6 chore(deps-dev): bump eslint-plugin-jsdoc from 15.3.9 to 15.5.2
  • 036adf0 Merge pull request #9417 from webpack/dependabot/npm_and_yarn/eslint-plugin-jest-22.8.0
  • 37d4480 Merge pull request #9411 from webpack/dependabot/npm_and_yarn/simple-git-1.121.0
  • ce2a183 chore(deps-dev): bump eslint-plugin-jest from 22.7.2 to 22.8.0
  • 0beeb7e Merge pull request #9391 from vankop/create-hash-typescript
  • bf1a24a #9391 resolve super call discussion
  • bd7d95b #9391 resolve discussions, AbstractMethodError
  • 4190638 chore(deps): bump ajv from 6.10.1 to 6.10.2

There are 42 commits in total.

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Clarify how to change the log level

I could not find a fully satisfying way to define the log level of my action built via the builder.

I tried multiple solutions provided by @tripodsan, like params: [ "LOG_LEVEL=debug"], params: [ "CORALOGIX_LOG_LEVEL=debug"] (in package.json) but none worked.

Only solution that seemed to work was:

"wsk": {
    "name": ...,
    "linksPackage": "helix-services",
    "params-file": [
      ".env"
    ]
  }

where .env contains CORALOGIX_LOG_LEVEL=debug.

Multiple things to clarify:

  • why CORALOGIX_LOG_LEVEL ? I do not use Coralogix in my setup, I would expect something like LOG_LEVEL
  • how to pass the param ? This is certainly related to #105

This could be fixed (if needed) and documented.

--params does not work

I'm not able to set params, as illustrated by this build: https://circleci.com/gh/adobe/helix-experimental-dispatch/106

$ npx wsk-builder --params EPSAGON_TOKEN $EPSAGON_TOKEN -v --deploy --test --name helix-services/`node -e "console.log(require('./package.json').name.replace('@adobe/helix-', ''))"`@ci$CIRCLE_BUILD_NUM
$ wsk action get helix-services/experimental-dispatch@ci106
ok: got action helix-services/experimental-dispatch@ci106
{
    "namespace": "helix/helix-services",
    "name": "experimental-dispatch@ci106",
    "version": "0.0.1",
    "exec": {
        "kind": "nodejs:10",
        "binary": true
    },
    "annotations": [
        {
            "key": "description",
            "value": "Example Package"
        },
        {
            "key": "web-export",
            "value": true
        },
        {
            "key": "raw-http",
            "value": false
        },
        {
            "key": "exec",
            "value": "nodejs:10"
        }
    ],
    "limits": {
        "timeout": 60000,
        "memory": 256,
        "logs": 10,
        "concurrency": 200
    },
    "publish": false
}

Provide option to return JSON output

[9:48:23 AM] [semantic-release] [@semantic-release/exec] β€Ί β„Ή  The command npm run deploy wrote invalid JSON to stdout. The stdout content will be ignored.

When using openwhisk-action-builder with semantic-release, the stdout output should be JSON.

Error code from failed deployment is not propagated

In this build https://circleci.com/gh/adobe/helix-static/10 the npm run deploy step failed:

Creating:  dist/helix-services/[email protected]
 - package.json
 - main.js
 - .env
 874959 total bytes
ok: created action: dist/helix-services/[email protected].
Deploying dist/helix-services/[email protected] as helix-services/[email protected] to OpenWhisk
[error]  PUT https://adobeioruntime.net/api/v1/namespaces/helix/actions/helix-services/[email protected]?overwrite=true Returned HTTP 404 (Not Found) --> "The requested resource does not exist."

but the error wasn't propagated, so that npm run semantic-release didn't fail.

Add development server

it would be nice to be able to start a local development that emulates openwhisk web actions.

Shouldn't OW settings in secrets.env take precendence over .wskprops?

In:

process.env.__OW_API_KEY = builder._wskAuth || params.WSK_AUTH;

entries in an eventual .wskprops file (e.g. AUTH) take precedence over parameters defined in secrets/secrets.env (e.g. WSK_AUTH) of a project.

It took me some time to figure out, why the onedrive-listener suddenly stopped working - entries in our secrets.env were being overwritten by my personal namespace settings in .wskprops - so I'm wondering whether this shouldn't be the other way round? Or should we generally avoid putting WSK_xxx settings in secrets.env?

creating sequence failure should fail the process

failure during semantic-release:

openwhisk-action-builder v1.2.0
Deploy to openwhisk the following command or specify --deploy on the commandline:
$ wsk action update bot/[email protected] --kind nodejs:10 --web raw dist/bot/[email protected]
error: failed creating sequence: PUT https://adobeioruntime.net/api/v1/namespaces/helix/actions/bot/mail-sink@latest?overwrite=true Returned HTTP 400 (Bad Request) --> "Sequence component does not exist."
error: failed creating sequence: PUT https://adobeioruntime.net/api/v1/namespaces/helix/actions/bot/[email protected]?overwrite=true Returned HTTP 400 (Bad Request) --> "Sequence component does not exist."
error: failed creating sequence: PUT https://adobeioruntime.net/api/v1/namespaces/helix/actions/bot/mail-sink@v1?overwrite=true Returned HTTP 400 (Bad Request) --> "Sequence component does not exist."

over overall run was successful

Pluggable Wrappers/Middleware

It seems like the creation of wrappers that work like express middleware and decorate the main function is a common pattern for our OpenWhisk actions. The list of wrappers we use on a regular basis so far include

  • adobe/helix-pingdom-status
  • epsagon/epsagon-node
  • openwhisk-loggly-wrapper
    and we can expect more to come. In order to unify and simplify the creation of these wrappers, it would be great to move from a model where you have to add them explicitly to your index.js to a model where you can specify wrap wrappers to apply on the wsk-builder command line.

Each wrapper would pick up configuration from the package.json and added as part of the finished output file. This would also enable the creation of smaller, more single-purpose wrappers such as:

  • one that turns ALL_CAPS_PARAMS into environment vars
  • one that removes and hides ALL_CAPS_PARAMS

extract helper to own package

the helpers like expressify and log utilities should be moved to their own repository.
suggest: openwhisk-action-utils

add version semantics to deploy

it should be easier to use the @ notation to deploy different versions of the action.
support --pkgVersion to override the package version during deployment.

not possible to specify target path of static file

it's not possible to define a relative path for a static file in the package.json config.
it should be possible by specifying an array. eg:

  "wsk": {
    "static": [
      "secrets/my.enc",
      ["src/views/test.hbs", "views/default.hbs"],
    ]
  },

in this case, the src/view/test.hbs will be bundled as views/defaults.hbs.

"verify command" proposed does not containt the package name when deploying a non-web action

When deploying my non-web action from https://github.com/kptdobe/helix-theblog-importer, the command proposed to verify the action does not include the correct package name:

$ wsk-builder --web-export false -v --deploy

...

Deploying dist/helix-services-private/[email protected] as helix-services-private/[email protected] to OpenWhisk
ok: updated action /acapt/helix-services-private/[email protected]

You can verify the action with:
$ wsk action invoke -r [email protected]
{
  "name": "openwhisk;host=https://adobeioruntime.net",
  "url": "/acapt/helix-services-private/[email protected]"
}

The correct message should be:

You can verify the action with:
$ wsk action invoke -r helix-services-private/[email protected]

or

You can verify the action with:
$ wsk action invoke -r /acapt/helix-services-private/[email protected]

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.