edgexfoundry / edgex-go Goto Github PK
View Code? Open in Web Editor NEWEdgeX Golang Services Monorepo | Owner: Core/Support WG
License: Apache License 2.0
EdgeX Golang Services Monorepo | Owner: Core/Support WG
License: Apache License 2.0
Based on updated requirements, refactor the Device Service SDK in Go.
Establish tests to meet performance targets, which include ability to run all of EdgeX (all the services to include the virtual device service) on a Raspberry Pi 3 (1 GB RAM, 64bit CPU, at least 32GB storage space, running Raspbian Linux distro). Additional tests include ability to startup in 1 minute or less (post OS boot โ from micro service start to all services up and available) and see throughput for one piece of data (with one IP device connected by hard wire โ versus WiFi) from data ingestion at the Device Service layer, through Core Data, to Rules Engine and back down through Command Service finally to Device Service for actuation will be < 1 second.
Move the EdgeX microservice, code and API documentation to Github. Provide linkage between the Wiki site and Github documentation as necessary. Remove the applicable documentation from the Wiki.
A couple of suggestions for improving our new logging service:
it would be nice if we the service operated similar to Go's log package, which provide functions which provides versions of all the logging functions that take printf style strings (e.g. Debugf, Errorf, ...)
we should add a facility to allow log levels to be specified per logging client, ideally in such a manner that the level can be adjusted dynamically via a REST API
remove logger.c name & line number from log messages. Currently log messages are being output with the following format:
INFO: YYYY/MM/DD HH:MM:SS logger.go:<line #>
Provide verbiage w/r/t supported command line flags and their usage via ./service --help
See
https://stackoverflow.com/questions/23725924/can-gos-flag-package-print-usage or this: https://github.com/MainfluxLabs/fluxmq/blob/master/main.go
Review and bring in Samsung initial config-seed in Go project. Update and apply new config key standards. Add profile bootstrap property to load only the necessary configuration.
Provide initial EdgeX system management features which include a system management API in each micro service and a system management agent to allow translation and EdgeX orchestration of system management operations (like stop, start, restart). Per https://wiki.edgexfoundry.org/download/attachments/329501/EdgeX%20Foundry%20Micro%20Service%20System%20Management-v5.docx?version=1&modificationDate=1515605688000&api=v2
Complete blackbox REST API tests for the remaining EdgeX micro services to include support-logging, support-notifications, support-scheduler, support-rulesengine, export-client, export-distro and the device services.
As part of the initial security services, provide a reverse proxy (probably done via Kong open source) with supporting JWT authentication
In the configuration.toml files, make the above change.
The Makefile needs an update to build/test all of the services in the edgex-go repo and associated docker images. This is the only way to ensure we have repo wide coverage.
In addition, if we ever add another service there will need to be a corresponding addition to the Makefile to build/test it.
drasko@Marx:~/go/src/github.com/edgexfoundry/edgex-go$ glide install
[INFO] Lock file (glide.lock) does not exist. Performing update.
[INFO] Downloading dependencies. Please wait...
[INFO] --> Fetching updates for github.com/eclipse/paho.mqtt.golang
[INFO] --> Fetching updates for gopkg.in/mgo.v2
[INFO] --> Fetching updates for go.uber.org/zap
[INFO] --> Fetching updates for github.com/pebbe/zmq4
[INFO] --> Fetching updates for github.com/hashicorp/consul
[INFO] --> Fetching updates for github.com/go-zoo/bone
[INFO] --> Fetching updates for github.com/robfig/cron
[INFO] --> Fetching updates for github.com/gorilla/mux
[INFO] --> Fetching updates for gopkg.in/yaml.v2
[INFO] Resolving imports
[INFO] --> Fetching updates for go.uber.org/atomic
[INFO] --> Detected semantic version. Setting version for go.uber.org/atomic to v1.3.1
[INFO] --> Fetching updates for go.uber.org/multierr
[INFO] --> Detected semantic version. Setting version for go.uber.org/multierr to v1.1.0
[ERROR] Error scanning github.com/edgexfoundry/core-domain-go/models: cannot find package "." in:
/home/drasko/.glide/cache/src/https-github.com-edgexfoundry-core-domain-go/models
[INFO] --> Fetching updates for github.com/gorilla/context
[INFO] --> Fetching updates for golang.org/x/net
[INFO] --> Fetching updates for github.com/hashicorp/go-cleanhttp
[INFO] --> Fetching updates for github.com/hashicorp/go-rootcerts
[INFO] --> Fetching updates for github.com/hashicorp/serf
[INFO] --> Fetching updates for github.com/mitchellh/go-homedir
[INFO] --> Fetching updates for github.com/armon/go-metrics
[INFO] --> Fetching updates for github.com/hashicorp/go-immutable-radix
[INFO] --> Fetching updates for github.com/hashicorp/golang-lru
[ERROR] Failed to retrieve a list of dependencies: Error resolving imports
as a stipulative rule, seems like all models are defined into core/domain/models
dir. The Notification
should also be there just like Schedule
. @drasko do you approve this ?
While testing my edgex core snap (version: 0.5.1+cali-20180322) on an Ubuntu 16.04 LTS Desktop system (amd64), I noticed the following error messages every time a new event is pushed from device-virtual to core-data output on the terminal used to start edgex:
_INFO: 2018/04/10 09:39:18 Posting Event: {"id":"","pushed":0,"device":"KMC.BAC-121036
CE01","created":0,"modified":0,"origin":1523367558014,"schedule":null,"event":null,"r
eadings":[{"id":"","pushed":0,"created":0,"origin":1523367558014,"modified":0,"device
":"KMC.BAC-121036CE01","name":"AnalogValue_20","value":"41.76"}]}
ERROR: 2018/04/10 09:39:18 mgoGetDeviceById Invalid Object ID KMC.BAC-121036CE01_
The Error message is being generated by the function mgoGetDeviceById() (src/core/metadata/mongoOps.go), in response to a call from Core Data's eventHandler function (src/core/data/event.go):
// Get device from metadata
deviceFound := true
// Try by ID
d, err := mdc.Device(e.Device)
if err != nil {
// Try by name
d, err = mdc.DeviceForName(e.Device)
if err != nil {
deviceFound = false
}
}
// Make sure the identifier is the device name
if deviceFound {
e.Device = d.Name
}
// See if metadata checking is enabled
if configuration.MetaDataCheck && !deviceFound {
loggingClient.Error("Device not found for event: "+err.Error(), "")
http.Error(w, err.Error(), http.StatusNotFound)
return
}
There are a few problems with this code:
models.Device
structs around by value, especially when they're not being used is just wasted compute cycles.models.Event
and models.Readings
have a field names "Device", which is confusing as I would think this would refer to a models.Device
. In reality these fields are deviceName, and probably should be updated (there's even a comment in each struct definition clarifying this)Ideally, by performing a
docker-compose up
all microservices should come up in the correct manner, without the need to run a shell script delaying the startup because the Go services are "too fast" and come up before Consul.
Since there is no sleep mechanism in docker compose, this should be in the logic of the Go microservices which should retry at specific intervals (5 sec?) to locate Consul and register themselves.
As the config file path would read from config
package (LoadFromFile), so we could remove the unnecessary config file const definition in modules core-command and core-metadata
License and Attribution files are necessary to satisfying licensing agreements. The Attribution.txt is missing from export-distro and export-client. DevOps is working on way to automate the inclusion of these files in deployment artifacts like Docker images.
Test of any APIs and protection offered by the reverse proxy and data protection services. We need to insure that they are actually performing as intended. This is not a full penetration test.
Currently our produced edgex-go
binaries are like this:
drasko@Marx:~/go/src/github.com/edgexfoundry/edgex-go$ ls build/
core-command core-data core-metadata export-client export-distro
I would propose that we prefix them with edgex-
in order to find them easy when listing the active processes in the system (i suppose that they can/will be launched with something like nohup <bin_name>&
)
assignee : @yanghua
Now, in support/logging/server.go, when respone request /ping
, the response header's Content-Type
is application/text
, this value is wrong. When use this value and send this request with browser, the behavior is donwload a file, not display a text pong
. It should be text/plain
.
Hitting the export client or servier PING API (192.168.99.100:48071/api/v1/ping) results in the browser asking if you want to save the response versus just display it. Check the content type returned - and see how core data does this.
Re-implement the rules engine (currently written in Java using Drools) in Go. Alternatively, pick a package and language implementation that offers the same or similar capability but in a smaller footprint (memory, CPU), smaller executable size, and starts up faster than current Java implementation.
Duplicate functionality found in the Java export client and export distro services to allow for export to Azure IoT Hub
As part of the initial security services, provide data protection services service - probably completed with Hashicorp Vault
In the various service configuration (json) files have inconsistent key capitalisation.
For example just looking at the Consul related keys there are:
Consulcheckaddress and ConsulCheckAddress
Consulhost and ConsulHost
Consulprofilesactive and ConsulProfilesActive
Consulport and ConsulPort
Looks like the core-data config files are those missing capitalisation.
Core data should allow a check of the existence of the device in any event to be checked in metadata. Does not appear to be working. This should also be a configurable option to turn or off the check based on level of trust the user has with data reported by device services (check at first and turn off later when the device is known to work well).
The new core-data configuration.json file specifies URLs for the various REST endpoints it uses from other services. IMHOP, the configuration should specify the host and port of any dependent services, whereas the actual endpoints and versions being used should be part of the code itself.
Reported as of commit: 74f6945
Per Core WG meeting of 3/29, more documentation is needed on the meaning and options of each of the services configuration properties. Perhaps this is in the code or in a separate file (TBD) - but some guidance to developers is required.
Now it used const hardcode, should read from config file. Can I apply this ticket to fix?
Core services currently read config file from hard-coded location: https://github.com/edgexfoundry/edgex-go/blob/master/cmd/core-command/main.go#L37
Go binaries should be portable, and probably end-up in $GOBIN
(via go install
). Also, very often $GOBIN
is added to system path, so that these binaries are accessible system-wide.
Because they should be capable to start from different locations, they should be capable to accept config file location via command-line parameter or ENV variable.
For the sake of simplicity, I would suggest ENV variable here (easy to pass into Docker via docker-compose
environment` parameter)
Some of the microservices have API endpoints with same REST path. Example: /ping
.
When behind reverse proxy, a mapping is needed so that we can know which exactly service we address (i.e. are we hitting export-client/ping
or core-data/ping
).
Suggested mapping can be /<service_name>/<api_ednpoint>
- for example /export-client/ping
, but Kong
(a reverse proxy and API gateway currently selected by Security WG) has also a method of passing this configuration to microservices via HTTP headers (might be impractical if we later want MQTT topics to map to HTTP URLs).
Support logging only have the 'file' backend, mongo still needs to be implemented.
drasko@Marx:~/go/src/github.com/edgexfoundry/edgex-go$ make
CGO_ENABLED=0 go build -ldflags "-X github.com/edgexfoundry/edgex-go.Version=0.5.0" -o cmd/export-client/export-client ./cmd/export-client
CGO_ENABLED=1 go build -ldflags "-X github.com/edgexfoundry/edgex-go.Version=0.5.0" -o cmd/export-distro/export-distro ./cmd/export-distro
CGO_ENABLED=0 go build -ldflags "-X github.com/edgexfoundry/edgex-go.Version=0.5.0" -o cmd/core-metadata/core-metadata ./cmd/core-metadata
CGO_ENABLED=1 go build -ldflags "-X github.com/edgexfoundry/edgex-go.Version=0.5.0" -o cmd/core-data/core-data ./cmd/core-data
# pkg-config --cflags libzmq
Package libzmq was not found in the pkg-config search path.
Perhaps you should add the directory containing `libzmq.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libzmq' found
pkg-config: exit status 1
Makefile:29: recipe for target 'cmd/core-data/core-data' failed
make: *** [cmd/core-data/core-data] Error 2
https://github.com/edgexfoundry/support-notifications has to be rewritten in Go.
Based on updated requirements, refactor the Device Service SDK in C
When uploading device profiles, the LSB value of "true" or "false" causes errors (it is not interpreted as a boolean). Setting it to true or false results in a null value for LSB. There may be other properties that are not getting loaded correctly.
Can I impelement a support-scheduler's go client ?
Duplicate functionality found in the Java export client and export distro services to allow for export to Google IoT Core
If agree this ticket, I could fix it.
Now it used const hardcode, should read from config file. Can I apply this ticket to fix?
SendXXX
method to AddXXX
, let it matches UpdateXXX
, RemoveXXX
conventionhi @jpwhitemn and @drasko I'm testing the schedule microservice I have done. But I find the time fields' type (start/end) of Schedule
model are inconsistent in two places.
The API document says using ISO 8601.
However, the current golang implementation parses there fields as millisecond string.
So which one is right?
Now it used const hardcode, should read from config file. apply this ticket to fix.
As the core-edgeX (command/data/metadata) microservices are a drop-in GO replacements, I thought I will use the device-mqtt service from the Eclipse IDE in the developer mode refering to California preview release - which is throwing an HTTP 503 error. PFA log below.
[2018-03-28 10:24:59.248] boot - 16160 DEBUG [main] --- BaseService: initialization attempt 1
[2018-03-28 10:25:05.325] boot - 16160 ERROR [main] --- SpringApplication: Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commandController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.edgexfoundry.handler.CommandHandler org.edgexfoundry.controller.CommandController.command; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commandHandler': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.edgexfoundry.Initializer org.edgexfoundry.handler.CommandHandler.init; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initializer': Invocation of init method failed; nested exception is java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:760)
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:360)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1185)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1174)
at org.edgexfoundry.Application.main(Application.java:36)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.edgexfoundry.handler.CommandHandler org.edgexfoundry.controller.CommandController.command; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commandHandler': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.edgexfoundry.Initializer org.edgexfoundry.handler.CommandHandler.init; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initializer': Invocation of init method failed; nested exception is java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 17 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'commandHandler': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.edgexfoundry.Initializer org.edgexfoundry.handler.CommandHandler.init; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initializer': Invocation of init method failed; nested exception is java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 19 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.edgexfoundry.Initializer org.edgexfoundry.handler.CommandHandler.init; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initializer': Invocation of init method failed; nested exception is java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 30 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initializer': Invocation of init method failed; nested exception is java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
... 32 more
Caused by: java.lang.Error: Unresolved compilation problems:
unlocked cannot be resolved or is not a field
enabled cannot be resolved or is not a field
at org.edgexfoundry.BaseService.setService(BaseService.java:274)
at org.edgexfoundry.BaseService.getService(BaseService.java:233)
at org.edgexfoundry.BaseService.attemptToInitialize(BaseService.java:153)
at org.edgexfoundry.BaseService.postConstructInitialize(BaseService.java:142)
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:498)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:354)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:305)
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
... 44 more```
Whereas the same device-mqtt service works well, when refering to barcelona release.
Appreciate any help in this regards.
PS: I'm pretty sure that I configured the IP addresses of Mosquitto broker, hostname and edgex services properly.
Duplicate functionality found in the Java export client and export distro services to allow for export to a ZeroMQ endpoint
Refactor support-notifications in Go lang (from Java). Move into the new mono repo. Apply the new config standards.
I have found a way to compile all the go code in the repository and to generate all the microservices. The only problem is that the executables are stored in $GOPATH/bin
go install ./...
So we will need to move them afterwards to other location.
We could use something like:
build:
go install ./...
mv $GOPATH/bin/core-data $DESTDIR
...
mv $GOPATH/bin/export-distro $DESTDIR
Removing the build_microservices target. Opinions?
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.