@windingtree/wt-write-api
v0.17.1
Published
API to write data to the Winding Tree platform
Downloads
48
Readme
WT Write API
API server written in node.js to interact with the Winding Tree platform. It is capable of:
- Create and register new hotels in a segment directory
- Update existing hotel records
- Delete hotels from a segment directory
It also automatically publishes notifications about changes via the WT Update API.
Requirements
- Nodejs >=10
Development
In order to install and run tests, we must:
git clone [email protected]:windingtree/wt-write-api.git
nvm install
npm install
npm run resolve-swagger-references
npm test
Running in dev mode
With all the dependencies installed, you can start the dev server.
First step is to initialize the SQLite database used to store your settings.
If you want to use a different database, feel free to change the connection
settings in the appropriate configuration file in src/config/
.
npm run createdb-dev
If you'd like to start afresh later, just delete the .dev.sqlite
file.
Second step is starting Ganache (local Ethereum network node). You can skip this step if you have a different network already running.
npm run dev-net
For trying out the interaction with the running dev-net and wt-write-api in general,
you can use the Winding Tree demo wallet protected by password windingtree
.
It is initialized on dev-net
with enough ether. For sample interaction scripts, check out our
Developer guides.
!!!NEVER USE THIS WALLET FOR ANYTHING IN PRODUCTION!!! Anyone has access to it.
{"version":3,"id":"7fe84016-4686-4622-97c9-dc7b47f5f5c6","address":"d037ab9025d43f60a31b32a82e10936f07484246","crypto":{"ciphertext":"ef9dcce915eeb0c4f7aa2bb16b9ae6ce5a4444b4ed8be45d94e6b7fe7f4f9b47","cipherparams":{"iv":"31b12ef1d308ea1edacc4ab00de80d55"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"d06ccd5d9c5d75e1a66a81d2076628f5716a3161ca204d92d04a42c057562541","n":8192,"r":8,"p":1},"mac":"2c30bc373c19c5b41385b85ffde14b9ea9f0f609c7812a10fdcb0a565034d9db"}};
Now we can run our dev server.
npm run dev
When using a dev
config, we internally run a script to deploy WT Index. It is not immediate,
so you might experience some errors in a first few seconds. And that's the reason why
it is not used in the same manner in integration tests.
You can fiddle with the configuration in src/config/
.
Running this server
Docker
You can run the whole API in a docker container, and you can
control which config will be used by passing an appropriate value
to WT_CONFIG variable at runtime. Database will be setup during the
container startup in the current setup. You can skip this with
SKIP_DB_SETUP
environment variable.
$ docker build -t windingtree/wt-write-api .
$ docker run -p 8080:8000 -e ETH_NETWORK_PROVIDER=address_to_node -e WT_CONFIG=playground windingtree/wt-write-api
- After that you can access the wt-write-api on local port
8080
- This deployment is using a Ropsten configuration that can be found in
src/config/playground
- Warning - User wallets (although protected by password) will be stored in the image, be careful where your API is running and who can access it.
- Warning - Once your docker container (and its associated volumes, if any) is deleted, all accounts (wallets and configuration) will disappear.
NPM
You can install and run this from NPM as well:
$ npm install -g @windingtree/wt-write-api
$ -e ETH_NETWORK_PROVIDER=address_to_node WT_CONFIG=playground wt-write-api
This will also create a local SQLite instance in the directory
where you run the wt-write-api
command. To prevent that,
you can suppress DB creation with SKIP_DB_SETUP
environment
variable.
Probably the easiest way of getting an Ethereum Node API is to register with Infura.
Running in production
You can customize the behaviour of the instance by many environment
variables which get applied if you run the API with WT_CONFIG=envvar
.
These are:
WT_CONFIG
- Which config will be used. Defaults todev
.ADAPTER_SWARM_GATEWAY
- Address of a Swarm HTTP Gateway, for examplehttps://swarm.windingtree.com
orhttps://swarm-gateways.net
ADAPTER_SWARM_READ_TIMEOUT
- Read timeout in milliseconds for Swarm, defaults to 1000ADAPTER_SWARM_WRITE_TIMEOUT
- Write timeout in milliseconds for Swarm, defaults to 2500WT_ENTRYPOINT_ADDRESS
- On chain address of Winding Tree EntrypointPORT
- HTTP Port where the API will lsiten, defaults to 8000.LOG_LEVEL
- Set log level for winston.BASE_URL
- Base URL of this API instance, for examplehttps://playground-api.windingtree.com
ETH_NETWORK_NAME
- Name of Ethereum network for informational purposes, for exampleropsten
ormainnet
ETH_NETWORK_PROVIDER
- Address of Ethereum node, for examplehttps://ropsten.infura.io/v3/my-project-id
DB_CLIENT
- Knex database client name, for examplesqlite3
.DB_CLIENT_OPTIONS
- Knex database client options as JSON string, for example{"filename": "./envvar.sqlite"}
.SKIP_DB_SETUP
- Whether to not setup new database upon startup.
For example the playground configuration can be emulated with the following command (the actual values are probably obsolete, check src/config/playground.js
for current values):
docker run -p 8080:8000 \
-e WT_CONFIG=envvar \
-e ADAPTER_SWARM_GATEWAY=https://swarm.windingtree.com \
-e WT_ENTRYPOINT_ADDRESS=0xa268937c2573e2AB274BF6d96e88FfE0827F0D4D \
-e ETH_NETOWRK_NAME=ropsten \
-e ETH_NETWORK_PROVIDER=https://ropsten.infura.io/v3/my-project-id \
-e DB_CLIENT_OPTIONS='{"filename": "./envvar.sqlite"}' \
-e DB_CLIENT=sqlite3 \
-e BASE_URL=https://playground-write-api.windingtree.com \
windingtree/wt-write-api \
In production, this API should not be accessible from the open internet as it internally holds potentially lots of funds in ETH.
How tos
Account setup
In order to use the API, you need to create an account that stores your configuration. The account consists of Ethereum wallet in JSON format and a configuration of uploaders. The uploaders are telling the API where to put data about hotels managed by that Ethereum wallet. The API does not store Wallet passwords.
The JSON-format wallet can easily be created locally with mycrypto.
In this case, we are setting up swarm as our preferred storage, make sure it is
actually accessible (via the swarmProvider
gateway url from config) before you
try to create hotel.
{
"wallet": {"version":3,"id":"7fe84016-4686-4622-97c9-dc7b47f5f5c6","address":"d037ab9025d43f60a31b32a82e10936f07484246","crypto":{"ciphertext":"ef9dcce915eeb0c4f7aa2bb16b9ae6ce5a4444b4ed8be45d94e6b7fe7f4f9b47","cipherparams":{"iv":"31b12ef1d308ea1edacc4ab00de80d55"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"d06ccd5d9c5d75e1a66a81d2076628f5716a3161ca204d92d04a42c057562541","n":8192,"r":8,"p":1},"mac":"2c30bc373c19c5b41385b85ffde14b9ea9f0f609c7812a10fdcb0a565034d9db"}},
"uploaders": {
"root": {
"swarm": {}
}
}
}
$ curl -X POST localhost:8000/accounts -H 'Content-Type: application/json' --data @create-account.json
# These values are generated and will be different
{"accountId":"aa43edaf8266e8f8","accessKey":"usgq6tSBW+wDYA/MBF367HnNp4tGKaCTRPy3JHPEqJmFBuxq1sA7UhFOpuV80ngC"}
Uploaders
We currently support two types of uploaders, and each uploader can contain a configuration. These configuration values are not encrypted in any way.
- Swarm - No configuration needed, we will use the Swarm gateway configured
for the whole wt-write-api instance and reasonable defaults for timeout. Timeouts
are in milliseconds. You can override these settings with:
providerUrl
- address of a Swarm gateway. Mandatory.timeout
- General timeout. Will be used for both read and write.timeoutRead
- Read timeout, overwrites the general timeout.timeoutWrite
- Write timeout, overwrites the general timeout.
- AWS S3 - We recommend to use a separate IAM account for this with a limited
set of permissions. All of the following options are required unless stated
otherwise.
accessKeyId
- AWS credentialssecretAccessKey
- AWS credentialsregion
- AWS regionbucket
- S3 bucket to upload to. This is the name that is used in URL.keyPrefix
- a prefix ("directory") to upload hotel data to. Serves to differentiate between different hotels stored in the same s3 bucket. Optional.
You can use any combination of these uploaders for the following list of objects:
root
-Hotel data index
object. This object is required.description
-Hotel description
objectratePlans
-RatePlans
objectavailability
-Availability
object ...
If you, for example, would like to store your description on Swarm, but everything else in your S3,
you would use the following account object for your initial POST
request. For any object type not
explicitely stated in the configuration, the root
configuration will be used.
{
"wallet": {"....": "...."},
"uploaders": {
"root": {
"s3": {
"accessKeyId": "AX...",
"secretAccessKey": "1234...",
"region": "eu-west-1",
"bucket": "hotel-data"
}
},
"description": {
"swarm": {
"providerUrl": "https://swarm-gateways.net",
"timeout": 3000,
"timeoutRead": 5000,
"timeoutWrite": 7000
}
}
}
}
In another example, you want everything on swarm except availability
and ratePlans:
{
"wallet": {"....": "...."},
"uploaders": {
"root": {
"swarm": {}
},
"availability": {
"s3": {
"accessKeyId": "AX...",
"secretAccessKey": "1234...",
"region": "eu-west-1",
"bucket": "hotel-data"
}
},
"ratePlans": {
"s3": {
"accessKeyId": "AX...",
"secretAccessKey": "1234...",
"region": "eu-west-1",
"bucket": "hotel-data"
}
}
}
}
However, there is a third option. If you plan to host all of the data by yourself, for example on your own webserver, you might not need to specify any uploaders:
{
"wallet": {"....": "...."},
"uploaders": {}
}
Create hotel
Registering a hotel
In case you plan to manage all of your data on your own, you can only register a hotel
with a single orgJson
and a keccak256 hash of its contents.
{
"orgJson": "https://example.com/my-hotel-org.json",
"hash": "0x123456..."
}
$ curl -X POST localhost:8000/hotels -H 'Content-Type: application/json' \
-H 'X-Access-Key: usgq6tSBW+wDYA/MBF367HnNp4tGKaCTRPy3JHPEqJmFBuxq1sA7UhFOpuV80ngC' \
-H 'X-Wallet-Password: windingtree' \
--data @hotel-description.json
# This value will be different
{"address":"0xA603FF7EA9A1B81FB45EF6AeC92A323a88211f40"}
You can verify that the hotel data was saved by calling
$ curl localhost:8000/hotels/0xa8c4cbB500da540D9fEd05BE7Bef0f0f5df3e2cc
In this case, the Write API will try to resolve all of the data from the provided URI as well.
Registering a hotel with data
A full-fledged usage of Write API will take care of uploading the data and generating the ORG.JSON hash
for you. This is just an example data that we know will pass all the validations. For a better
description of the actual data model, have a look at src/services/validators/
,
where you will find JSON schemas used to validate incoming data.
The legalEntity is part of the ORG.ID standard. Go check it out.
{
"legalEntity": {
"name": "Hostel Marilena",
"contact": {
"email": "[email protected]",
"phone": "+40213191564",
"url": "https://geotowns.ro/marilena"
},
"address": {
"road": "Horse Saddle",
"houseNumber": "34",
"postcode": "33200",
"city": "Dragolm",
"countryCode": "RO"
}
},
"hotel": {
"description": {
"name": "Random hotel",
"description": "**Beautiful** hotel located in the center of _Prague, Czech Republic_.",
"location": {
"latitude": 50.075388,
"longitude": 14.414170
},
"contacts": {
"general": {
"email": "[email protected]",
"phone": "00420224371111",
"url": "https://jirkachadima.cz",
"ethereum": "windingtree.eth"
}
},
"address": {
"line1": "Rašínovo nábřeží 1981/80",
"line2": "Nové Město",
"postalCode": "12000",
"city": "Prague",
"country": "CZ"
},
"timezone": "Europe/Prague",
"currency": "CZK",
"amenities": [],
"images": [
"https://raw.githubusercontent.com/windingtree/media/master/logo-variants/tree/png/tree--gradient-on-white.png",
"https://raw.githubusercontent.com/windingtree/media/master/logo-variants/full-logo/png/logo--black-on-green.png"
],
"updatedAt": "2018-06-19T15:53:00+0200",
"defaultCancellationAmount": 30,
"roomTypes": {
"1234-abcd": {
"name": "string",
"description": "string",
"totalQuantity": 0,
"occupancy": {
"min": 1,
"max": 3
},
"amenities": [
"TV"
],
"images": [
"https://raw.githubusercontent.com/windingtree/media/web-assets/logo-variants/full-logo/png/logo--white.png"
],
"updatedAt": "2018-06-27T14:59:05.830Z",
"properties": {
"nonSmoking": "some"
}
}
}
}
}
}
$ curl -X POST localhost:8000/hotels -H 'Content-Type: application/json' \
-H 'X-Access-Key: usgq6tSBW+wDYA/MBF367HnNp4tGKaCTRPy3JHPEqJmFBuxq1sA7UhFOpuV80ngC' \
-H 'X-Wallet-Password: windingtree' \
--data @hotel-description.json
# This value will be different
{"address":"0xA603FF7EA9A1B81FB45EF6AeC92A323a88211f40"}
You can verify that the hotel data was saved by calling
$ curl localhost:8000/hotels/0xa8c4cbB500da540D9fEd05BE7Bef0f0f5df3e2cc
Update hotel
You can also update previously created hotels. The top-level
properties (e.g. description
) are always replaced as a whole.
{
"legalEntity": {
"name": "Hostel Marilena",
"contact": {
"email": "[email protected]",
"phone": "+40213191564",
"url": "https://geotowns.ro/marilena"
},
"address": {
"road": "Horse Saddle",
"houseNumber": "34",
"postcode": "33200",
"city": "Dragolm",
"countryCode": "RO"
}
},
"hotel": {
"description": {
"name": "Changed hotel name",
"description": "**Beautiful** hotel located in the center of _Prague, Czech Republic_.",
"location": {
"latitude": 50.075388,
"longitude": 14.414170
},
"contacts": {
"general": {
"email": "[email protected]",
"phone": "00420224371111",
"url": "https://jirkachadima.cz",
"ethereum": "windingtree.eth"
}
},
"address": {
"line1": "Rašínovo nábřeží 1981/80",
"line2": "Nové Město",
"postalCode": "12000",
"city": "Prague",
"country": "CZ"
},
"timezone": "Europe/Prague",
"currency": "CZK",
"amenities": [],
"images": [
"https://raw.githubusercontent.com/windingtree/media/master/logo-variants/tree/png/tree--gradient-on-white.png",
"https://raw.githubusercontent.com/windingtree/media/master/logo-variants/full-logo/png/logo--black-on-green.png"
],
"updatedAt": "2018-06-19T15:53:00+0200",
"defaultCancellationAmount": 30,
"roomTypes": {
"1234-abcd": {
"name": "string",
"description": "string",
"totalQuantity": 0,
"occupancy": {
"min": 1,
"max": 3
},
"amenities": [
"TV"
],
"images": [
"https://raw.githubusercontent.com/windingtree/media/web-assets/logo-variants/full-logo/png/logo--white.png"
],
"updatedAt": "2018-06-27T14:59:05.830Z",
"properties": {
"nonSmoking": "some"
}
}
}
}
}
}
Alternatively, you can update only the orgJson
as well:
{
"orgJson": "https://example.com/my-updated-hotel-org.json",
"hash": "0x123456..."
}
$ curl -X PATCH localhost:8000/hotels -H 'Content-Type: application/json' \
-H 'X-Access-Key: usgq6tSBW+wDYA/MBF367HnNp4tGKaCTRPy3JHPEqJmFBuxq1sA7UhFOpuV80ngC' \
-H 'X-Wallet-Password: windingtree' \
--data @hotel-description.json
The data format version indicator in hotel data index is always updated to the write api's declared data format version, no matter how many parts of a hotel you update.
Publicly available instances
For currently available public instances of wt-write-api, please see this page.