Giter Site home page Giter Site logo

dyne / restroom-mw Goto Github PK

View Code? Open in Web Editor NEW
3.0 6.0 11.0 1005 KB

๐Ÿ›  Easy REST API builder executing Zencode

Home Page: https://restroom.dyne.org

License: GNU Affero General Public License v3.0

JavaScript 25.83% TypeScript 72.06% Mustache 1.62% Dockerfile 0.49%
zenroom zencode rest-api blockchain crypto smart-contracts dyne-ebsi

restroom-mw's Introduction

restroom

Restroom
Easy REST API builder executing Zencode

Dyne.org



Restroom is crafted with care by developers of the DECODE project about data-ownership and technological sovereignty. Our effort is that of improving people's awareness of how their data is processed by algorithms, as well facilitate the work of developers to create along privacy by design principles using algorithms that can be deployed in any situation without any change.

๐Ÿšฉ Table of Contents (click to expand)

๐Ÿš€ Quick start

Start by reading the documentation

back to ๐Ÿ”


๐Ÿ˜ Acknowledgements

Copyright ยฉ 2020 by Dyne.org foundation, Amsterdam

Designed, written and maintained by Puria Nafisi Azizi.

Special thanks to Jaromil for his special contributions and for the idea.

Image in README courtesy of Old book illustrations

back to ๐Ÿ”


๐ŸŒ Links

https://zenroom.org/

https://dev.zenroom.org/

https://dyne.org/

https://decodeproject.eu/

back to ๐Ÿ”


๐Ÿ‘ค Contributing

Please first take a look at the Dyne.org - Contributor License Agreement then

  1. ๐Ÿ”€ FORK IT
  2. Create your feature branch git checkout -b feature/branch
  3. Commit your changes git commit -am 'Add some fooBar'
  4. Push to the branch git push origin feature/branch
  5. Create a new Pull Request
  6. ๐Ÿ™ Thank you

back to ๐Ÿ”


๐Ÿ’ผ License

Restroom-mw - Easy REST API builder executing Zencode
Copyright (c) 2020 Dyne.org foundation, Amsterdam

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

back to ๐Ÿ”

restroom-mw's People

Contributors

albertolerda avatar andrea-dintino avatar matteo-cristino avatar pasfranc avatar phoebus-84 avatar puria avatar rebeccaselvaggini avatar seabassdk avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

restroom-mw's Issues

Dockerfile not working when opening /docs

# Importing node14 docker image
FROM node:14

# Install nano for debug
RUN apt-get update
RUN apt-get install nano

# Installing restroom
RUN npx degit dyne/restroom-template restroom-mw

# setup docker
WORKDIR /restroom-mw
EXPOSE 3300 
EXPOSE 3301 

# Adding the .env file
RUN touch .env
RUN echo 'ZENCODE_DIR=/restroom-mw/zencode\n\
CUSTOM_404_MESSAGE=nothing to see here\n\
HTTP_PORT=3300\n\
HTTPS_PORT=3301\n'\
> /restroom-mw/.env

# Adding the exported files
RUN echo "Adding exported contracts from apiroom"
RUN echo "Scenario 'ecdh': Encrypt a message with the password \nGiven that I have a 'string' named 'password' \nGiven that I have a 'string' named 'header' \nGiven that I have a 'string' named 'message' \nWhen I encrypt the secret message 'message' with 'password' \nThen print the 'secret message'"> ./zencode/encrypt-with-pass-all-in-keys.zen
RUN echo '{"password":"myVerySecretPassword","header":"A very important secret","message":"Dear Bob, your name is too short, goodbye - Alice."}' > ./zencode/encrypt-with-pass-all-in-keys.keys
RUN echo "Scenario 'ecdh': Create the keypair\nGiven that I am known as 'Alice'\nWhen I create the keypair\nThen print my data\n"> ./zencode/keypair.zen
RUN echo "Scenario 'credential': create request \nGiven that I am known as 'Alice' \nand I have my valid 'credential keypair' \nWhen I create the credential request \nThen print my 'credential request'"> ./zencode/credential.zen
RUN echo '{"Alice":{"credential_keypair":{"private":"zK8Gs70vdLRErIKkTQdNbWfrWIExirgMKSxfGuPa1aU=","public":"AxSggI9xzQ/RTjH2gRyFd+QHPCTqZucAihUyETUhVdJY9vohtmcGDFmEdOTHOTCeow=="}}}' > ./zencode/credential.keys
RUN echo "Rule check version 1.0.0\nScenario 'ecdh': keypair management and ECDSA signature\n\n# Here we load everything we need\nGiven that I am 'JackInTheShop' \nGiven that I have my valid 'keypair' \nGiven that I have a 'string' named 'dictionaryToBeFound'\nGiven that I have a 'string dictionary' named 'TransactionsBatchA'\nGiven that I have a 'number' named 'salesStartTimestamp'\n\n# Here we search if a certain dictionary exists in the list\nWhen the 'dictionaryToBeFound' is found in 'TransactionsBatchA'\n\n# Here we find the highest value of an element, in all dictionaries\nWhen I find the max value 'PricePerKG' for dictionaries in 'TransactionsBatchA'\nand I rename the 'max value' to 'maxPricePerKG'\n\n# Here we sum the values of an element, from all dictionaries\nWhen I create the sum value 'TransactionValue' for dictionaries in 'TransactionsBatchA' \nand I rename the 'sum value' to 'sumValueAllTransactions'\n\n# Here we sum the values of an element, from all dictionaries, with a condition\nWhen I create the sum value 'TransferredProductAmount' for dictionaries in 'TransactionsBatchA' where 'timestamp' > 'salesStartTimestamp'\nand I rename the 'sum value' to 'transferredProductAmountafterSalesStart'\n\n# Here we create a dictionary\nWhen I create the 'number dictionary'\nand I rename the 'number dictionary' to 'salesReport'\n\n# Copy a dictionary nested to the root level of data\nWhen I create the copy of 'Information' from dictionary 'TransactionsBatchA'\nand I rename the 'copy' to 'InformationBatchA'\n\n# Here we insert elements into the newly created dictionary\nWhen I insert 'maxPricePerKG' in 'salesReport' \nWhen I insert 'sumValueAllTransactions' in 'salesReport' \nWhen I insert 'transferredProductAmountafterSalesStart' in 'salesReport' \n\n# Hash dictionaries, using default (sha256) or sha512\nWhen I create the hash of 'InformationBatchA' \nWhen I rename the 'hash' to 'sha256hashOfInformationBatchA' \n\nWhen I create the hash of 'salesReport' using 'sha512' \nWhen I rename the 'hash' to 'sha512hashOfsalesReport' \n\n# Here we produce an ECDSA signature the newly created dictionary using\nWhen I create the signature of 'salesReport'\nand I rename the 'signature' to 'salesReport.signature'\n\n#Print out the data we produced along \n# We also print the dictionary "Information" as hex, just for fun\nThen print the 'salesReport'\nThen print the 'salesReport.signature'\nThen print the 'Information' as 'hex' inside 'TransactionsBatchA' \nThen print the 'InformationBatchA'\nThen print the 'sha256hashOfInformationBatchA' \nThen print the 'sha512hashOfsalesReport' "> ./zencode/Operation-with-dictionaries.zen
RUN echo '{"JackInTheShop":{"keypair":{"private_key":"Aku7vkJ7K01gQehKELav3qaQfTeTMZKgK+5VhaR3Ui0=","public_key":"BBCQg21VcjsmfTmNsg+I+8m1Cm0neaYONTqRnXUjsJLPa8075IYH+a9w2wRO7rFM1cKmv19Igd7ntDZcUvLq3xI="}}}' > ./zencode/Operation-with-dictionaries.keys

# Debugging lines
RUN ls -al
RUN cat .env
RUN ls -al ./zencode
RUN cat .env

# npm install and run
run npm i
# run npm i fuzzball
run npm run start




 

DB Package: change the reading of record from parameter to object containing the parameter

The statement should be changed to this:

Zencode:

Rule caller restroom-mw

Given I have a database uri named 'mysql'
Given I have a database table named 'myTable'

Given I have a 'string dictionary' named 'myResult'

Given I read the record 'myRecord' of the table 'myTable' of the database 'mysql' and save the result into 'myResult'

Then print 'myResult'

Keys:

{
        "myRecord": 11,
	"myTable": "firstTable",
	"mysql": "mysql://48Dg7xv6YL:[email protected]:3306/48Dg7xv6YL" 
}

Detect cycles in chain execution and launch an exception in case

In the event we have a cycle into the chain, i.e. contr A next is B and contr. B next is A the chain executor is never stopping and forever running in a a loop. We need to prevent it navigating the blocks before the chain execution (going next by next) and counting. If counter > # of blocks defined in a yaml file => we have a loop. Execution is aborted because a cycle is detected

Error 500 on HTTP POST

This script:

Rule caller restroom-mw

Given I have a 'string' named 'add-identity'
Given I have a 'string dictionary' named 'identity'
Given I have a 'string dictionary' named 'output'

Given I connect to 'add-identity' and pass it the content of 'identity' and save the output into 'output'

Then print data

with this data:

{
	"add-identity": "https://apiroom.net/api/dyneorg/consensusroom-add-identity",
	"identity": {
		"Kenshiro": {
			"baseUrl": "http://192.168.0.100:3030",
			"ip": "192.168.0.100",
			"public_key": "BGiQeHz55rNc/k/iy7wLzR1jNcq/MOy8IyS6NBZ0kY3Z4sExlyFXcILcdmWDJZp8FyrILOC6eukLkRNt7Q5tzWU=",
			"timeServer": "http://localhost:3312",
			"uid": "Kenshiro",
			"version": "1"
		}
	}
}

produces an error 500. The API is hosted at: https://apiroom.net/api/dyneorg/consensusroom-announce-p2


You can test the endpoint to which the POST is targeted using:

curl -X 'POST' \
  'https://apiroom.net/api/dyneorg/consensusroom-add-identity' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "data": {

"identity": {
		"Kenshiro": {
			"baseUrl": "http://192.168.0.100:3030",
			"ip": "192.168.0.100",
			"public_key": "BGiQeHz55rNc/k/iy7wLzR1jNcq/MOy8IyS6NBZ0kY3Z4sExlyFXcILcdmWDJZp8FyrILOC6eukLkRNt7Q5tzWU=",
			"timeServer": "http://localhost:3312",
			"uid": "Kenshiro",
			"version": "1"
		}
	}



},
  "keys": {}
}'

Reading "HEAD" and "previous" from blockchains

In order to have a multilayer blockchain, for all the blockchains we support, we need statements that:

  • read the HEAD of the blockchain
  • given a certain "block", a statement that read the content of the previous block (I guess it's an address in Sawroom?)

Something like:

Given I read from Sawroom the data in the HEAD and save the output into 'sawroomHead'
Given I read from Sawroom the data in the address preceding 'myAddress' and save the output into 'sawroomPrevious'

Data:

{
"myAddress": "c274b51d3851ebcd31347fa373b4ca2e9be9eb0cadcc209f769af8156b9f754241013f"
}

Load uri from data or keys

Currently in the middleware db a uri, is loaded inline from Zencode:

Given I have a database connection uri at 'mysql://user:[email protected]:3306/mydatabase'

The line should be changed to:

Given I have a database connection named 'myFirstDatabase'

which would load the uri from data or keys in JSON, like:

{

"myFirstDatabase" : "mysql://user:[email protected]:3306/mydatabase"

}

Script works only with parameters passed in data

The script

Rule caller restroom-mw
Given I have a valid redis connection on 'redis://localhost:6379'
Given I read from redis the data under the key 'listOfIdentities' and save the output into 'redisResult'
Given I have a 'string dictionary' named 'redisResult'

Given I have a 'string dictionary' named 'identity'

When I create the copy of 'identities' from dictionary 'redisResult'
When I rename the 'copy' to 'identities'

When I insert 'identity' in 'identities'

Then print the 'identities'
Then I write all data into redis under the key 'listOfIdentities'


With the parameters:

{
	"add-identity": "https://apiroom.net/api/dyneorg/consensusroom-add-identity",
	"identity": {
		"Kenshiro": {
			"baseUrl": "http://192.168.0.100:3030",
			"ip": "192.168.0.100",
			"public_key": "BGiQeHz55rNc/k/iy7wLzR1jNcq/MOy8IyS6NBZ0kY3Z4sExlyFXcILcdmWDJZp8FyrILOC6eukLkRNt7Q5tzWU=",
			"timeServer": "http://localhost:3312",
			"uid": "Kenshiro",
			"version": "1"
		}
	}
}

When deployed as API, works only if parameters are passed as data.

Curls:

  • Working:
curl -X 'POST' \
  'https://apiroom.net/api/dyneorg/consensusroom-add-identity' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
	"data": {
		"identity": {
			"Kenshiro": {
				"baseUrl": "http://192.168.0.100:3030",
				"ip": "192.168.0.100",
				"public_key": "BGiQeHz55rNc/k/iy7wLzR1jNcq/MOy8IyS6NBZ0kY3Z4sExlyFXcILcdmWDJZp8FyrILOC6eukLkRNt7Q5tzWU=",
				"timeServer": "http://localhost:3312",
				"uid": "Kenshiro",
				"version": "1"
			}
		}
	},
	"keys": {}
}'
  • NOT-Working:
curl -X 'POST' \
  'https://apiroom.net/api/dyneorg/consensusroom-add-identity' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
	"data": {},
	"keys": {
		"identity": {
			"Kenshiro": {
				"baseUrl": "http://192.168.0.100:3030",
				"ip": "192.168.0.100",
				"public_key": "BGiQeHz55rNc/k/iy7wLzR1jNcq/MOy8IyS6NBZ0kY3Z4sExlyFXcILcdmWDJZp8FyrILOC6eukLkRNt7Q5tzWU=",
				"timeServer": "http://localhost:3312",
				"uid": "Kenshiro",
				"version": "1"
			}
		}
	}
}'

Sawroom wallet doesn't support fractions

Intkey doesn't support fractions, only ints.

In order to manage fractions, we convert them in Restroom-mw, like:
1 Sawroom "intkey" coin = 0.000001 Sawroom coins

Possible memory leak in restroom-mw or zenroom(NPM)

At some point, apiroom/restroom crashes, this is what happens behind the scene

0|apiroom  | GET /api/dyneorg/-generate-random-array 500 677.413 ms - 652
0|apiroom  | RangeError: WebAssembly.Memory(): could not allocate memory
0|apiroom  |     at Object.default (/home/apiroom/public_html/node_modules/zenroom/dist/main/zenroom.js:1347:18)
0|apiroom  |     at Object.exports.zencode_exec (/home/apiroom/public_html/node_modules/zenroom/dist/main/index.js:9:46)
0|apiroom  |     at /home/apiroom/public_html/node_modules/@restroom-mw/core/dist/index.js:66:19
0|apiroom  |     at Generator.next (<anonymous>)
0|apiroom  |     at fulfilled (/home/apiroom/public_html/node_modules/@restroom-mw/core/dist/index.js:5:58)
0|apiroom  |     at processTicksAndRejections (internal/process/task_queues.js:93:5)

DB statetement that write, need to be checked

Based on this: https://apiroom.net/api/dyneorg/read-write-database-acme

ZENCODE:

Rule unknown ignore 
Scenario 'ecdh': Create the keypair
Given my name is 'John'
Given I have a database uri named 'mysql'
Given I have a database table named 'myTable'
Given I have a 'string dictionary' named 'myZenroomStringDictionary'
# the value of the record could be 0 to max could be 
# Given I read the record '1' of the table 'myTable' of the database 'mysql' and save the result into 'myZenroomStringDictionary'
When I create the array of '8' random objects of '256' bits
Then print all data
# Then I save the 'array' into the database 'mysql' into the table 'myTable'

Then I save the output into the database 'mysql' into the table 'myTable'

KEYS

{
	"myTable": "firstTable",
	"myCache": "firstCache",
	"mysql": "mysql://user:pass@server:3306/db",
	"myZenroomStringDictionary": {
		"data": {
			"data1": "9WgBlK+Kcq3AKpmhituXQe4UPkzH3zpZiQa4Szm1Q40=",
			"data2": "BCEo8MgRiSxtLfxE4UEDVnbdZ21h+xc+egLIRk3VTagpJxlBfu9MjqXGUi2N7tIewpcDrr5V7Z2cmMcNsbKWSGQ="
		}
	}
}

[http] Allow POST calls

STATEMENT:
Given I connect to 'endpoint' and pass it the content of 'myDataToPOST' and save the output into 'dataFromEndpoint'

With the following DATA:

"myDataToPOST": {
  "id": "XXXXXXXXXXXXXXXXXXXXXXXXX",
  "recurseLimit": 2
 },

with the endpoint:

{
"endpoint": "http://reflow-demo.dyne.org:4000/api/json/trace"
}

acting like:

curl -XPOST -Hcontent-type:application/json -d'{"id": "XXXXXXXXXXXXXXXXXXXXXXXXX", "recurseLimit": 2}' -k http://reflow-demo.dyne.org:4000/api/json/trace

Add statement to run SQL query

A statement like:

I execute the query 'myQuery' into the database 'mySqlite'

With data:

{
"mySqlite": "'sqlite:///home/site/public_html/sqlite.db", 
"myQuery": "CREATE TABLE [IF NOT EXISTS] Persons (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
);"

}

http middleware does not work as expected

Contract used:

Rule unknown ignore
Scenario 'ecdh': Create the keypair
Given that I have an endpoint named "myEndpoint"
Given that I connect to "myEndpoint" and save the output
Then print data
Then print the "output"

with the following data:

{
	"myEndpoint": "https://apiroom.net/api/dyneorg/Create-a-keypair"
}

The expected result: the middleware should run and fill the output field in the json result.

Zenroom crash in Restroom

The script:

# The Rule unknown ignore is required when using a restroom-mw statement
Rule unknown ignore 

# we'll need to create a keypair to produce an ECDSA signature later
Scenario 'ecdh': Create the keypair
Given that I am known as 'timestampServer'
Given I have my 'keypair'

# Those are restroom-mw statements: define the endpoints
Given that I have an endpoint named 'timeServer' 

# We need those object to store the output of the endpoints
Given I have a 'number' named 'timestamp'


# Those are restroom-mw statements: connect to endpoints and store their output into Zenroom's objects
Given I connect to 'timeServer' and save the output into 'timestamp'


When I create the signature of 'timestamp' 
When I rename the 'signature' to 'timestamp.signature' 


When I create the copy of 'public key' from dictionary 'keypair'
When I rename the 'copy' to 'public key'

# Printing the output
Then print the 'timestamp'
Then print the 'public key'
Then print the 'timestamp.signature'

With the data:

{
  "timeServer": "http://apiroom.net:3312",
  "timestampServer": {
    "keypair": {
      "private_key": "mi9nqTAatFaaGYZrToHGGsHrJXyP4MQWcEFN35mGrsE=",
      "public_key": "BNYP6D9E5MX8brrbbc+RmH2k2L+f0v0PBTudRl4+g3x6q5dVEnIrqLK/i5NK6E6c2hY7YtU2mv0X6k4deYvgMPE="
    }
  }
}

Results into:

{
"zenroom_errors": {
"result": "",
"logs": " . System memory manager in use\n . MUTT print functions in use\n . ECDH curve is SECP256K1\n . Memory in use: 301 KB\n . Rule unknown ignore [W] Zencode pattern ignored: Given that I have an endpoint named 'timeServer'\t1[W] Zencode pattern ignored: Given I connect to 'timeServer' and save the output into 'timestamp'\t1[W] Zencode is missing version check, please add: rule check version N.N.N[W] When I create the signature of 'timestamp'\t2[W] . Scenario 'ecdh'[W] . Given that I am known as 'timestampServer'[W] . Given I have my 'keypair'[W] . Given I have a 'number' named 'timestamp'[W] . When I create the signature of 'timestamp'[W] . \u001b[31;1m[!]\u001b[0m /zencode.lua:323: attempt to index a number value (local 'A')[W] {\n a_GIVEN_in = {},\n b_WHEN_ack = {\n keypair = {\n private_key = octet[32] mi9nqTAatFaaGYZrToHGGsHrJXyP4MQWcEFN35mGrsE=,\n public_key = octet[65] BNYP6D9E5MX8brrbbc+RmH2k2L+f0v0PBTudRl4+g3x6q5dVEnIrqLK/i5NK6E6c2hY7YtU2mv0X6k4deYvgMPE=\n },\n timestamp = 1619703176\n },\n c_THEN_out = {}\n}[!] Zencode execution error\n[!] Script:\nZEN:begin()\nZEN:parse([[\n# The Rule unknown ignore is required when using a restroom-mw statement\r\nRule unknown ignore \r\n\r\n# we'll need to create a keypair to produce an ECDSA signature later\r\nScenario 'ecdh': Create the keypair\r\nGiven that I am known as 'timestampServer'\r\nGiven I have my 'keypair'\r\n\r\n# Those are restroom-mw statements: define the endpoints\r\nGiven that I have an endpoint named 'timeServer' \r\n\r\n# We need those object to store the output of the endpoints\r\nGiven I have a 'number' named 'timestamp'\r\n\r\n\r\n# Those are restroom-mw statements: connect to endpoints and store their output into Zenroom's objects\r\nGiven I connect to 'timeServer' and save the output into 'timestamp'\r\n\r\n\r\nWhen I create the signature of 'timestamp' \r\nWhen I rename the 'signature' to 'timestamp.signature' \r\n\r\n\r\nWhen I create the copy of 'public key' from dictionary 'keypair'\r\nWhen I rename the 'copy' to 'public key'\r\n\r\n# Printing the output\r\nThen print the 'timestamp'\r\nThen print the 'public key'\r\nThen print the 'timestamp.signature'\n\n]])\nZEN:run()\n\n[!] /zencode.lua:285: When I create the signature of 'timestamp' \n[!] Error detected. Execution aborted.\n[*] Zenroom teardown.\n . Memory used: 335 KB\n"
},
"result": "",
"exception": "[ZENROOM EXECUTION ERROR]\n\n\nundefined"
}

Marshall json output to url64 before storing to blockchain

For all the blockchain we support, we need be able to deterministically marshall a JSON using an encoding that can be read by Zenroom (url64 is preferred), when storing data on chain. Something like:

Then I ask 'Sawroom' to store the data named 'myJSONOutput' as 'url64' into the tag 'myTag'

Implement calls to ethereum client

Wishlist for Restroom

Given I have the 'balance' of 'address' for erc20 'contract address'
Given I have the 'balance' of 'address' for erc20 'contract address' named 'variable name'
Given I have the 'decimals' for erc20 'contract address' ( named 'variable name' )
Given I have the 'name'     for erc20 'contract address' ( named 'variable name' )
Given I have the 'symbol'   for erc20 'contract address' ( named 'variable name' )
Given I have the 'total supply' for erc20 'contract address' ( named 'variable name' )

rr.setData seems not pass data to Zenroom

We're testing using the http mw, with:

Contract:

Rule unknown ignore 
Scenario 'ecdh': Create the keypair
Given that I am known as 'Alice'
Given that I have an endpoint named 'myEndPoint'
Given that I have an endpoint named 'myEndPoint2'
Given that I have an endpoint named 'myEndPoint3'
Given I connect to 'myEndPoint' and save the output
#When I rename the 'output' to 'sebEnpointReading'
# Given I connect to 'myEndPoint2' and save the output
When I create the random object of '1024' bits
Then pass the output to 'myOutputEndPoint'
Then pass the output to 'myOutputEndPoint1'
Then pass the output to 'myOutputEndPoint2'
Then print all data

And data:

{
  "myEndPoint": "https://jsonplaceholder.typicode.com/todos/1",
  "myEndPoint2": "https://apiroom.net/api/dyneorg/Create-a-keypair",
  "myOutputEndPoint": "http://localhost:3020/post"
}

Timeout for script execution

At some point we'll need the possibility to define a timeout (in ms) for a script execution, configurable via config or via zencode

Allow storing of output into a user-defined table

Currently the db middleware stores data in a db using the statement:

Then save the result into the database

which stores the output into a table named "results".
The statement should be modified to allow the user to pick a database name and a table name like:

Then save the result into the database 'myFirstDatabase' into the table 'myFirstTable'

REDIS add

I need the possibility to add a key-value to an existing object, something like:

Rule caller restroom-mw
Given I have a valid redis connection on 'redis://localhost:6379'
When I create the random object of '256' bits
Then print all data
Then I add all data into redis under the key 'myHashkey'

Crash when opening localhost:3300/docs

With the latest version, localhost:3300/api/contract works fine, while /docs makes it crash and gives this error:

TypeError: Cannot create property 'root' on string './zencode'
/home/bario/Desktop/temp/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:287
      throw ex;
      ^

RuntimeError: abort(TypeError: Cannot create property 'root' on string './zencode') at Error
    at jsStackTrace (/home/bario/Desktop/temp/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:2081:17)
    at stackTrace (/home/bario/Desktop/temp/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:2098:16)
    at process.abort (/home/bario/Desktop/temp/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:1853:44)
    at process.emit (events.js:315:20)
    at processPromiseRejections (internal/process/promises.js:245:33)
    at processTicksAndRejections (internal/process/task_queues.js:94:32)
    at process.abort (/home/bario/Desktop/temp/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:1859:9)
    at process.emit (events.js:315:20)
    at processPromiseRejections (internal/process/promises.js:245:33)
    at processTicksAndRejections (internal/process/task_queues.js:94:32)
npm ERR! code ELIFECYCLE
npm ERR! errno 7
npm ERR! [email protected] start: `node dist/app.js`
npm ERR! Exit status 7
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/bario/.npm/_logs/2020-10-28T13_30_14_009Z-debug.log

Test not passing in node v10

For some reason the db tests are not executed on node 10... this issue here to dig later in the meantime let disable the CI checks on 10.x family.

The error is

 โœ” db โ€บ tests โ€บ index โ€บ Middleware db should exists (243ms)
 .  System memory manager in use
 .  ECDH curve is SECP256K1
 .  Memory in use: 221 KB
[*] Zenroom teardown.
 .  Memory used: 247 KB
 .  System memory manager in use
 .  ECDH curve is SECP256K1
 .  Memory in use: 221 KB
[*] Zenroom teardown.
 .  Memory used: 247 KB
Executing (default): CREATE TABLE IF NOT EXISTS `results` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `result` TEXT, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL);
Executing (default): INSERT INTO `tablename` (`id`,`result`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3);
Executing (default): PRAGMA INDEX_LIST(`results`)
Executing (default): INSERT INTO `results` (`id`,`result`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3);
SyntaxError: Unexpected token { in JSON at position 246
SyntaxError: Unexpected token { in JSON at position 246

  Uncaught exception in packages/db/tests/index.js

  RuntimeError: abort(SyntaxError: Unexpected token { in JSON at position 246) at Error: 

  โ€บ jsStackTrace (node_modules/zenroom/dist/lib/zenroom.js:2081:17)
  โ€บ stackTrace (node_modules/zenroom/dist/lib/zenroom.js:2098:16)
  โ€บ process.abort (node_modules/zenroom/dist/lib/zenroom.js:1853:44)
  โ€บ processEmit [as emit] (node_modules/signal-exit/index.js:161:32)
  โ€บ emitPromiseRejectionWarnings (internal/process/promises.js:140:18)
  โ€บ process._tickCallback (internal/process/next_tick.js:69:34)
  โ€บ process.abort (node_modules/zenroom/dist/lib/zenroom.js:1859:9)
  โ€บ processEmit [as emit] (node_modules/signal-exit/index.js:161:32)
  โ€บ emitPromiseRejectionWarnings (internal/process/promises.js:140:18)
  โ€บ process._tickCallback (internal/process/next_tick.js:69:34)

/Users/puria/src/github.com/dyne/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:287
      throw ex;
      ^

RuntimeError: abort(SyntaxError: Unexpected token { in JSON at position 246) at Error: 
    at jsStackTrace (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:2081:17)
    at stackTrace (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:2098:16)
    at process.abort (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:1853:44)
    at process.emit (events.js:203:15)
    at processEmit [as emit] (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/signal-exit/index.js:161:32)
    at emitPromiseRejectionWarnings (internal/process/promises.js:140:18)
    at process._tickCallback (internal/process/next_tick.js:69:34)
    at process.abort (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/zenroom/dist/lib/zenroom.js:1859:9)
    at process.emit (events.js:203:15)
    at processEmit [as emit] (/Users/puria/src/github.com/dyne/restroom-mw/node_modules/signal-exit/index.js:161:32)
    at emitPromiseRejectionWarnings (internal/process/promises.js:140:18)
    at process._tickCallback (internal/process/next_tick.js:69:34)
  โœ– packages/db/tests/index.js exited with a non-zero exit code: 7
  โ”€

Reading data from an endpoint

We have been requested the possibility to read from an endpoint, from within RESTroom, something like:

in data/keys the statement would expect something like:

{
"myEndPoint" : "https://apiroom.net/api/dyneorg/Create-a-keypair"
}

The statements would look like:

Given that I have an endpoint named 'myEndPoint' 

Given I connect to 'myEndPoint' and save the output into 'myApiOutput'

which would connect the endpoint, read the output and store the output into the object 'myApiOutput', without parsing it or sorting it.

Not sure if this needs to a REST POST and/or GET.

And then, we need a statement that passes the output as paramater to an API, like

Given that I have an endpoint named 'myOutputEndPoint' 

Then pass the output to 'myOutputEndPoint' into 'data'

Where the output endpoints should look like:

{
"myOutputEndPoint" : "https://apiroom.net/api/dyneorg/Create-a-keypair"
}

Restroom lets stuff.keys override stuff passed via REST

Situation:

In Restroom-mw, I have:

script.zen

rule check version 1.0.0
Scenario 'ecdh': Bob verifies the signature from Alice
Given I have a 'public key' from 'Alice'
Given I have a 'string' named 'myMessage'
Given I have a 'signature' named 'myMessage.signature'
When I verify the 'myMessage' has a signature in 'myMessage.signature' by 'Alice'
Then print 'Zenroom certifies that signatures are all correct!' as 'string'
Then print the 'myMessage'

And a script.keys files that contains just {}

Then I pass via REST this data in the keys{} parameter


{
  "data": {},
  "keys": {"myMessage": "Dear Bob, your name is too short, goodbye - Alice.",
	"myMessage.signature": {
		"r": "vWerszPubruWexUib69c7IU8Dxy1iisUmMGC7h7arDw=",
		"s": "nSjxT+JAP56HMRJjrLwwB6kP+mluYySeZcG8JPBGcpY="
	},
	"Alice": {
		"public_key": "BBCQg21VcjsmfTmNsg+I+8m1Cm0neaYONTqRnXUjsJLPa8075IYH+a9w2wRO7rFM1cKmv19Igd7ntDZcUvLq3xI="
	}}
}

And I get this error:

{
  "zenroom_errors": {
    "result": "",
    "logs": " .  System memory manager in use\n .  ECDH curve is SECP256K1\n .  Memory in use: 300 KB\n .  Zencode version match: 1.0.0+d38b84d .  rule check version 1.0.0 [W] Given I have a 'public key' from 'Alice'\t2[W]  .  Scenario 'ecdh'[W]  .  Given I have a 'public key' from 'Alice'[W]  .  ERR Cannot find 'public_key' inside 'Alice'[W]  .  \u001b[31;1m[!]\u001b[0m /zencode_given.lua:191: Cannot find 'public_key' inside 'Alice'[W] {\n    a_GIVEN_in = {\n        KEYS = {\n            [\"\"] = \"\"\n        }\n    },\n    b_WHEN_ack = {},\n    c_THEN_out = {}\n}[!] Zencode execution error\n[!] Script:\nZEN:begin()\nZEN:parse([[\nrule check version 1.0.0 \nScenario 'ecdh': Bob verifies the signature from Alice \n Given I have a 'public key' from 'Alice' \nGiven I have a 'string' named 'myMessage' \nGiven I have a 'signature' named 'myMessage.signature' \nWhen I verify the 'myMessage' has a signature in 'myMessage.signature' by 'Alice' \nThen print 'Zenroom certifies that signatures are all correct!' as 'string' \nThen print the 'myMessage' \n\n]])\nZEN:run()\n\n[!] /zencode.lua:285:  Given I have a 'public key' from 'Alice' \n[!] Error detected. Execution aborted.\n[*] Zenroom teardown.\n .  Memory used: 335 KB\n"
  },
  "result": "",
  "exception": "[ZENROOM EXECUTION ERROR]\n\n\nundefined"
}

I guess that Restroom-mw overrides the data passed via REST with the stuff stored in the script.keys? Is this is a feature or a bug?

Update: actually Restroom-mw does NOT read any data passed via REST as keys{} parameter.

<3

Reading data from GraphQL

Similarly to what happens with the request to read data from an APIs, we need a similar function for GraphQL, so we need statements that would process the input

{ 
"myFilter" : "query { 
      queryPost(filter: { datePublished: { ge: "2020-06-15" }}) {  } 
 }",
"myGraphQLuri" : "https://myuri.com/ciao"
}

and the statements should look like:

Given that I have GraphQL filter named "myFilter" 
Given that I have GraphQL uri named "myGraphQLuri"
Given I connect to 'myGraphQL' and execute the filter "myFilter" and save the output 

Passing SQL Query to DB middleware

Two statements in the DB-mw like:

Given I have a database uri named 'myDb1'
Given I have a database table named 'myTable'
Given I execute the query 'myQuery1' on the database 'myDb1' and save the result into 'myZenroomStringDictionary'
Given I have a 'string dictionary' named 'myZenroomStringDictionary'
When I create the random object of '128' bits
Then I execute the query 'myQuery2' on the database 'myDb1' with the data 'random_object'

Queries (they are randomly selected sample queries):

myQuery1: 
  SELECT *
  FROM call
  ORDER BY
      call.employee_id ASC,
      call.start_time ASC;

myQuery2: 
  SELECT *
  FROM call
  ORDER BY
      call.employee_id ASC,
      call.start_time ASC;

MQTT middleware in Restroom-mw

A middleware that can process the statements:

Given I have an MQTT endpoint named 'myEndpoint'
Given I connect to 'myEndpoint' and save the MQTT output into 'myJsonObject'
Then I pass the output as MQTT to 'otherEndpoint'

There are several npm packages, like https://www.npmjs.com/package/mqtt

rr.setData seems not pass data to Zenroom (updated)

update:

zencode

Rule unknown ignore
Scenario 'ecdh': Create the keypair
# Given that I am known as 'Alice'
Given that I have an endpoint named "myEndpoint"
Given that I connect to "myEndpoint" and save the output
# When I create the keypair
Then print data
Then print the "output"

data:

{
	"myEndpoint": "https://apiroom.net/api/dyneorg/Create-a-keypair"
}

DB package: reading the 'max' record doesn't work

There should be a way to read the last record on the table. How to reproduce:

Zencode:

Rule caller restroom-mw

Given I have a database uri named 'mysql'
Given I have a database table named 'myTable'

Given I have a 'string dictionary' named 'myResult'

Given I read the record 'max' of the table 'myTable' of the database 'mysql' and save the result into 'myResult'

Then print 'myResult'

Keys:

{
	"myTable": "firstTable",
	"mysql": "mysql://48Dg7xv6YL:[email protected]:3306/48Dg7xv6YL"
}

Issue with http post, http package

IMPORTANT: before executing, open the link https://beeceptor.com/console/dyneorg to create the endpoint on beeceptor.com.

The zencode:

Rule unknown ignore
# rule check version 1.0.0 
Scenario 'ecdh': Bob verifies the signature from Alice 

Given that I have an endpoint named 'outputEndpoint'  
Given I have a 'public key' from 'FairBnB' 

Given I have a 'string dictionary' named 'listingInfo' 
Given I have a 'signature' named 'listingInfo.signature' 

When I verify the 'listingInfo' has a signature in 'listingInfo.signature' by 'FairBnB' 

Then print the string 'the follwing listing is verified' 
Then print 'listingInfo'

Then pass the output to 'outputEndpoint'

The "keys":

{
	"FairBnB": {
		"public_key": "BM1uOdTzhuOTPmjkGMG1dmc/5ekX+0hUprddvD9/rENYn4OmLJfo4GVhZasrpA33h3IyxGioT8C/x8eVFE42X8E="
	},
	"outputEndpoint": "https://dyneorg.free.beeceptor.com",
	"listingInfo": {
		"listingCity": "Pescara",
		"listingCountry": "Italy",
		"listingID": "123456",
		"listingNotes": "Central, clean, sunny",
		"listingOwner": "ab12345jk"
	},
	"listingInfo.signature": {
		"r": "ZrgQjvl5zaB+O4W8SRcb7HBAOawPwZNONQpgy0YTkB4=",
		"s": "IBbR+xrnX4jbJ63rDJksHp+p4oBpJbYzU8zAJD5WRxI="
	}
}

Storing multiple object in Sawroom

In a situation where I have this data:

{
   "Alice": {
      "keypair": {
         "private_key": "bPPTAa32iZryENRmRC8UiHslfxxSD8JOPNVx6z2pO1I=",
         "public_key": "BB5pxLmnLBNF9NeEpZrERxf7Vk9XE768iJv/OGn/FuR04bzccVylRrZbac5TwqLfZotVPKctuUAfdaohmudXhpY="
      }
   }, 

"myString": "Hello World!",
"myNumber": 35
}

I need to be able to execut:

Then I ask Sawroom to store the data named 'Alice' into the tag 'myTag'
Then I ask Sawroom to store the data named 'myString' into the tag 'myTag'
Then I ask Sawroom to store the data named 'myNumber' into the tag 'myTag'

When later executing:

Given I read from Sawroom the data in tag 'myTag' and save the output into 'sawroom'

I will receive as output the exact same data

REDIS statement breaks after a few repeats

The script below, after a few executions, returns the error:

1643639911743E[!] JSON as malformed beginning or end
1643639911743E[!] ERROR:
1643639911743E[!] EXEC: /zenroom_json.lua:35: JSON as malformed beginning or end

The script:

Rule caller restroom-mw
Given I have a valid redis connection on 'redis://localhost:6379'
Given I have a 'string' named 'redisResult'
Given I read from redis the data under the key 'myHashkey' and save the output into 'redisResult'


When I create the 'string dictionary'

When I create the random object of '256' bits

When I insert 'random object' in 'string dictionary'

When I create the 'string array'

When I insert 'string dictionary' in 'string array'

Then print the 'string array'
Then I write all data into redis under the key 'myHashkey'

When it breaks, the key in Redis contains the value:

{"string_array":[{"random_object":"๏ฟฝ\t๏ฟฝะ“๏ฟฝ๏ฟฝ}๏ฟฝฺก+<v๏ฟฝC๏ฟฝy)Z๏ฟฝ\u0002\u0014๏ฟฝ๏ฟฝ\u0017๏ฟฝ"}]}

issue with parallel http get

It seems (!) to me that, when running parallel http get, the output can not be stored into an existing object.

The script:

Rule caller restroom-mw

Given I have a 'string dictionary' named '1'
Given I have a 'string dictionary' named '2'
Given I have a 'string dictionary' named '3'
Given I have a 'string dictionary' named '4'
Given I have a 'string dictionary' named '5'
Given I have a 'string dictionary' named '6'

Given I have a 'string' named 'ts1'
Given I have a 'string' named 'ts2'
Given I have a 'string' named 'ts3'
Given I have a 'string' named 'ts4'
Given I have a 'string' named 'ts5'
Given I have a 'string' named 'ts6'

Given I execute parallel GET to 'ts1' and save the result named 'timestamp1' within the object '1'
Given I execute parallel GET to 'ts2' and save the result named 'timestamp2' within the object '2'
Given I execute parallel GET to 'ts3' and save the result named 'timestamp3' within the object '3'
Given I execute parallel GET to 'ts4' and save the result named 'timestamp4' within the object '4'
Given I execute parallel GET to 'ts5' and save the result named 'timestamp5' within the object '5'
Given I execute parallel GET to 'ts6' and save the result named 'timestamp6' within the object '6'

Then print data

with data:

{
	"1": {
		"baseUrl": "http://192.168.0.109:3030",
		"ip": "192.168.0.109",
		"public_key": "BH4DAzUaA3GXJ4blwyX14J8ZzExD8XZI1mU3VuS7MH+xrGJafIbjRZcEJ0t0SVDTK9OgiYbn3x3vPhlGbqwbJd8=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Souther",
		"version": "1"
	},
	"2": {
		"baseUrl": "http://192.168.0.111:3030",
		"ip": "192.168.0.111",
		"public_key": "BC/DiN9hGAD5Ff5AKDd3DiYhXPD0bk37nmw48f1mNYoVs9O9koTHH7ResvhpVcPXr8XIw5ank+hni9LLwKTW5gQ=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Lynn",
		"version": "1"
	},
	"3": {
		"baseUrl": "http://192.168.0.110:3030",
		"ip": "192.168.0.110",
		"public_key": "BHZtsy1qifMCrXzF5nD1RFB4YY0SdbnHA1Pu4i0DfYIfNetlXgIQ9p7b7zzGJyP0XuJtQKA619MR3OIxDRheAV0=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Burt",
		"version": "1"
	},
	"4": {
		"baseUrl": "http://192.168.0.103:3030",
		"ip": "192.168.0.103",
		"public_key": "BESKGbFfv5V2W+/p22ESRb6g4cz8hIdKEh/KGfiZ6jjlFh9XZN7Rz93qiAeCqJlQ6HdyuzF64Gu8dj1zlmNK2fU=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Jagger",
		"version": "1"
	},
	"5": {
		"baseUrl": "http://192.168.0.108:3030",
		"ip": "192.168.0.108",
		"public_key": "BHCJefNXQZUfzh/uu6RBr5qN/QE5zyxXlayeBP5Bd4zR8aYHh0QWMMQS5skWYSuDolWFFjjvgKYHHmUdaCaXqZk=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Julia",
		"version": "1"
	},
	"6": {
		"baseUrl": "http://192.168.0.105:3030",
		"ip": "192.168.0.105",
		"public_key": "BLQWijG60cudq9uXErzowuBXP4fOWxFUkIfJxg6hnbDBQXa+nJWEd0WzFP8WMuTeUn5lsialbIltF1z1WLpW4eg=",
		"timestampAPI": "/api/consensusroom-get-timestamp",
		"uid": "Rei",
		"version": "1"
	},
	"ts1": "http://apiroom.net:3312/",
	"ts2": "http://apiroom.net:3312/",
	"ts3": "http://apiroom.net:3312/",
	"ts4": "http://apiroom.net:3312/",
	"ts5": "http://apiroom.net:3312/",
	"ts6": "http://apiroom.net:3312/"
}

produces this output:

{
  "1": {
    "baseUrl": "http://192.168.0.109:3030",
    "ip": "192.168.0.109",
    "public_key": "BH4DAzUaA3GXJ4blwyX14J8ZzExD8XZI1mU3VuS7MH+xrGJafIbjRZcEJ0t0SVDTK9OgiYbn3x3vPhlGbqwbJd8=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Souther",
    "version": "1"
  },
  "2": {
    "baseUrl": "http://192.168.0.111:3030",
    "ip": "192.168.0.111",
    "public_key": "BC/DiN9hGAD5Ff5AKDd3DiYhXPD0bk37nmw48f1mNYoVs9O9koTHH7ResvhpVcPXr8XIw5ank+hni9LLwKTW5gQ=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Lynn",
    "version": "1"
  },
  "3": {
    "baseUrl": "http://192.168.0.110:3030",
    "ip": "192.168.0.110",
    "public_key": "BHZtsy1qifMCrXzF5nD1RFB4YY0SdbnHA1Pu4i0DfYIfNetlXgIQ9p7b7zzGJyP0XuJtQKA619MR3OIxDRheAV0=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Burt",
    "version": "1"
  },
  "4": {
    "baseUrl": "http://192.168.0.103:3030",
    "ip": "192.168.0.103",
    "public_key": "BESKGbFfv5V2W+/p22ESRb6g4cz8hIdKEh/KGfiZ6jjlFh9XZN7Rz93qiAeCqJlQ6HdyuzF64Gu8dj1zlmNK2fU=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Jagger",
    "version": "1"
  },
  "5": {
    "baseUrl": "http://192.168.0.108:3030",
    "ip": "192.168.0.108",
    "public_key": "BHCJefNXQZUfzh/uu6RBr5qN/QE5zyxXlayeBP5Bd4zR8aYHh0QWMMQS5skWYSuDolWFFjjvgKYHHmUdaCaXqZk=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Julia",
    "version": "1"
  },
  "6": {
    "baseUrl": "http://192.168.0.105:3030",
    "ip": "192.168.0.105",
    "public_key": "BLQWijG60cudq9uXErzowuBXP4fOWxFUkIfJxg6hnbDBQXa+nJWEd0WzFP8WMuTeUn5lsialbIltF1z1WLpW4eg=",
    "timestampAPI": "/api/consensusroom-get-timestamp",
    "uid": "Rei",
    "version": "1"
  },
  "ts1": "http://apiroom.net:3312/",
  "ts2": "http://apiroom.net:3312/",
  "ts3": "http://apiroom.net:3312/",
  "ts4": "http://apiroom.net:3312/",
  "ts5": "http://apiroom.net:3312/",
  "ts6": "http://apiroom.net:3312/"
}

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.