├── project ├── build.properties └── plugins.sbt ├── src ├── main │ ├── resources │ │ ├── version.txt │ │ ├── swagger │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── index.css │ │ │ ├── swagger-initializer.js │ │ │ ├── index.html │ │ │ └── oauth2-redirect.html │ │ ├── logback.xml │ │ └── config.json │ └── scala │ │ └── com │ │ └── horizon │ │ └── exchangeapi │ │ ├── PATCH.java │ │ ├── tables │ │ ├── ExchangePostgresProfile.scala │ │ ├── AgentConfigurationManagement.scala │ │ └── Users.scala │ │ ├── SwaggerDocService.scala │ │ └── auth │ │ ├── Exceptions.scala │ │ └── Module.scala └── test │ ├── go │ ├── .gitignore │ └── Makefile │ ├── bash │ ├── anonymous.sh │ ├── get │ │ ├── nodes │ │ │ ├── 1-urlparms-noid.sh │ │ │ ├── 1-urlparms-user.sh │ │ │ ├── 1-urlparms.sh │ │ │ ├── all-root-urlparms.sh │ │ │ └── notthere.sh │ │ ├── users │ │ │ ├── user-newpw.sh │ │ │ ├── user-baduser.sh │ │ │ └── all-root-encoded.sh │ │ ├── admin │ │ │ ├── gettest │ │ │ │ └── root.sh │ │ │ └── dropdb-token │ │ │ │ └── root.sh │ │ ├── agbots │ │ │ └── notthere.sh │ │ └── agreements │ │ │ ├── agbots-notthere.sh │ │ │ └── nodes-notthere.sh │ ├── root.sh │ ├── node.sh │ ├── agbot.sh │ ├── post │ │ ├── admin │ │ │ ├── initdb │ │ │ │ └── root.sh │ │ │ ├── dropdb │ │ │ │ └── root.sh │ │ │ ├── reload │ │ │ │ ├── root.sh │ │ │ │ └── user.sh │ │ │ └── hashpw │ │ │ │ ├── root.sh │ │ │ │ └── user.sh │ │ ├── agbots │ │ │ ├── heartbeat │ │ │ │ ├── 1.sh │ │ │ │ └── 1-user.sh │ │ │ ├── dataheartbeat │ │ │ │ ├── 123.sh │ │ │ │ └── 123-list.sh │ │ │ ├── msgs │ │ │ │ └── a1-msg1.sh │ │ │ └── isrecentdata │ │ │ │ └── 123-list.sh │ │ ├── users │ │ │ ├── reset │ │ │ │ ├── anonymous.sh │ │ │ │ ├── user-root.sh │ │ │ │ └── user.sh │ │ │ ├── confirm │ │ │ │ ├── user-urlparms.sh │ │ │ │ ├── user.sh │ │ │ │ ├── user-urlparms2.sh │ │ │ │ ├── user-newpw.sh │ │ │ │ ├── baduser.sh │ │ │ │ └── user-encoded.sh │ │ │ ├── 2-create.sh │ │ │ ├── 2-create-badinput.sh │ │ │ ├── 2-create-noemail.sh │ │ │ ├── user-create-anonymous.sh │ │ │ ├── user-create-root.sh │ │ │ ├── 2-create-admin.sh │ │ │ ├── changepw │ │ │ │ ├── user.sh │ │ │ │ └── user-backtoorig.sh │ │ │ ├── user-create-frontend.sh │ │ │ ├── new-create-admin.sh │ │ │ └── 3-create-2.sh │ │ ├── nodes │ │ │ ├── heartbeat │ │ │ │ ├── 1.sh │ │ │ │ └── 1-user.sh │ │ │ └── msgs │ │ │ │ ├── 2-msg1.sh │ │ │ │ ├── 1-msg1.sh │ │ │ │ └── 1-msg2.sh │ │ ├── agreements │ │ │ └── confirm │ │ │ │ ├── 123.sh │ │ │ │ ├── 123-a2.sh │ │ │ │ └── 123-user.sh │ │ ├── orgs │ │ │ └── create.sh │ │ ├── patterns │ │ │ ├── nodehealth │ │ │ │ └── p1.sh │ │ │ ├── search │ │ │ │ └── location.sh │ │ │ └── p1.sh │ │ ├── services │ │ │ ├── dockauths │ │ │ │ └── gps.sh │ │ │ ├── gps.sh │ │ │ ├── location.sh │ │ │ ├── location-bad-reqsvc.sh │ │ │ └── location-diff-arch-bad.sh │ │ └── search │ │ │ └── devices │ │ │ ├── sdr-noversion.sh │ │ │ ├── netspeed-dave.sh │ │ │ ├── netspeed-wildcard-agbot.sh │ │ │ ├── sdr-wildcard.sh │ │ │ ├── sdr-wildcard-prod.sh │ │ │ ├── netspeed-wildcard.sh │ │ │ ├── sdr-wildcard-stale.sh │ │ │ ├── sdr-badpw.sh │ │ │ ├── sdr-arch-x86.sh │ │ │ ├── sdr-good-agbot.sh │ │ │ ├── sdr-memory-400.sh │ │ │ ├── sdr-version-2.sh │ │ │ ├── sdr-datav-false.sh │ │ │ ├── sdr-good.sh │ │ │ └── multiple-wildcard.sh │ ├── patch │ │ ├── agbots │ │ │ ├── 1-agbot-token.sh │ │ │ ├── 1-agbot-publicKey-blank.sh │ │ │ ├── 1-agbot-badinput.sh │ │ │ └── 1-agbot-publicKey.sh │ │ ├── nodes │ │ │ ├── 1-node-publicKey-blank.sh │ │ │ ├── 1-node-badinput.sh │ │ │ ├── 1-node-name.sh │ │ │ ├── 1-node-publicKey.sh │ │ │ └── 1-node-pattern.sh │ │ ├── pattern │ │ │ └── patch.sh │ │ └── services │ │ │ ├── gps-arch-bad.sh │ │ │ ├── gps.sh │ │ │ ├── gps-sharable-bad.sh │ │ │ └── location.sh │ ├── put │ │ ├── admin │ │ │ ├── loglevel │ │ │ │ └── root.sh │ │ │ ├── config │ │ │ │ ├── maxAgbots.sh │ │ │ │ ├── maxDevices.sh │ │ │ │ ├── maxAgreements.sh │ │ │ │ └── maxMessagesInMailbox.sh │ │ │ └── table │ │ │ │ └── users.sh │ │ ├── users │ │ │ ├── user-update-email.sh │ │ │ ├── 2-update-root.sh │ │ │ ├── user-update.sh │ │ │ ├── root-root.sh │ │ │ ├── 2-create-root.sh │ │ │ ├── user-update-root.sh │ │ │ ├── root-user.sh │ │ │ └── user-create-root.sh │ │ ├── agreements │ │ │ ├── 123-agbot.sh │ │ │ ├── 456-agbot.sh │ │ │ ├── 789-agbot.sh │ │ │ ├── 123-agbot-user.sh │ │ │ ├── 789-node.sh │ │ │ ├── 123-node.sh │ │ │ └── 456-node.sh │ │ ├── agbots │ │ │ ├── a1-compatible.sh │ │ │ ├── a3.sh │ │ │ ├── a1-root.sh │ │ │ ├── a1.sh │ │ │ ├── a2-user.sh │ │ │ ├── a2-root.sh │ │ │ └── a1-frontend.sh │ │ ├── services │ │ │ ├── dockauths │ │ │ │ └── gps-1.sh │ │ │ ├── keys │ │ │ │ └── key.sh │ │ │ ├── gps.sh │ │ │ ├── gps-arch-bad.sh │ │ │ └── gps-sharable-bad.sh │ │ ├── nodes │ │ │ ├── weird-user.sh │ │ │ ├── status │ │ │ │ └── 1-node.sh │ │ │ ├── 4-badop.sh │ │ │ ├── 999-badinput.sh │ │ │ ├── 4-badbool.sh │ │ │ ├── 4-badint.sh │ │ │ ├── 2-memory-400-node.sh │ │ │ ├── 2-memory-400.sh │ │ │ ├── 3-version-2-user.sh │ │ │ ├── 3-version-2-root.sh │ │ │ ├── 1-user.sh │ │ │ ├── 1-node.sh │ │ │ ├── 1-user-compatible.sh │ │ │ └── 1-user-changes.sh │ │ └── patterns │ │ │ ├── p1.sh │ │ │ └── p1-no-nodehealth.sh │ ├── user2.sh │ ├── README.md │ ├── user.sh │ ├── https.sh │ ├── functions.sh │ ├── frontend.sh │ ├── scale │ │ ├── summarize.sh │ │ ├── deleteperforg.sh │ │ ├── wrapper.sh │ │ └── scaledriver.sh │ └── dropalltables.sh │ └── scala │ └── com │ └── horizon │ └── exchangeapi │ ├── route │ ├── agentconfigurationmanagement │ │ └── TestAgentConfigMgmt.scala │ ├── admin │ │ ├── TestAdminDropdbTokenResponse.scala │ │ ├── TestAdminHashpwResponse.scala │ │ ├── TestAdminHashpwRequest.scala │ │ └── TestAdminConfigRequest.scala │ ├── org │ │ └── TestNodeHealthAgreementElement.scala │ ├── service │ │ └── TestGetServiceAttributeResponse.scala │ ├── node │ │ ├── TestPatternNodeResponse.scala │ │ └── TestPatternSearchHashElement.scala │ └── policy │ │ ├── TestBusinessPolicyNodeResponse.scala │ │ └── TestBusinessPolicySearchHashElement.scala │ ├── ExchConfigSuite.scala │ ├── SlickSuite.scala │ ├── VersionSuite.scala │ ├── PerfSuite.scala │ ├── TestDBConnection.scala │ └── ApiUtilsSuite.scala ├── config └── exchange-api.tmpl ├── .gitignore ├── tools └── Dockerfile-render ├── .github └── workflows │ └── exchange-build.yml └── .travis.yml /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.6.2 -------------------------------------------------------------------------------- /src/main/resources/version.txt: -------------------------------------------------------------------------------- 1 | 2.102.0 2 | -------------------------------------------------------------------------------- /src/test/go/.gitignore: -------------------------------------------------------------------------------- 1 | darwin/ 2 | linux/ 3 | -------------------------------------------------------------------------------- /src/main/resources/swagger/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playground/exchange-api/master/src/main/resources/swagger/favicon-16x16.png -------------------------------------------------------------------------------- /src/main/resources/swagger/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playground/exchange-api/master/src/main/resources/swagger/favicon-32x32.png -------------------------------------------------------------------------------- /src/test/bash/anonymous.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as anonymous 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | curl $copts -X $method -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/${org}$resource | $parse -------------------------------------------------------------------------------- /src/test/bash/get/nodes/1-urlparms-noid.sh: -------------------------------------------------------------------------------- 1 | # Gets node 1 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/nodes/1'?token=abc123' | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/nodes/1-urlparms-user.sh: -------------------------------------------------------------------------------- 1 | # Gets node 1 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/nodes/1'?id='$EXCHANGE_USER'&token='$EXCHANGE_PW | $parse -------------------------------------------------------------------------------- /src/test/bash/get/nodes/1-urlparms.sh: -------------------------------------------------------------------------------- 1 | # Gets node 1 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1'?id=n1&token=abc123' | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/nodes/all-root-urlparms.sh: -------------------------------------------------------------------------------- 1 | # Gets all nodes as root 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/nodes'?id=root&token='$EXCHANGE_ROOTPW | $parse -------------------------------------------------------------------------------- /src/test/bash/get/users/user-newpw.sh: -------------------------------------------------------------------------------- 1 | # Gets a user 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:newpw" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER | $parse -------------------------------------------------------------------------------- /src/test/bash/get/admin/gettest/root.sh: -------------------------------------------------------------------------------- 1 | # Gets all users as root 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" $EXCHANGE_URL_ROOT/v1/admin/gettest | $parse -------------------------------------------------------------------------------- /src/test/bash/root.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as root 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | curl $copts -X $method -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" $HZN_EXCHANGE_URL/${org}$resource | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/node.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as node 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | curl $copts -X $method -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" $EXCHANGE_URL_ROOT/v1/${org}$resource | $parse -------------------------------------------------------------------------------- /src/test/bash/agbot.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as agbot 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | curl $copts -X $method -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_AGBOTAUTH" $EXCHANGE_URL_ROOT/v1/${org}$resource | $parse -------------------------------------------------------------------------------- /src/test/bash/post/admin/initdb/root.sh: -------------------------------------------------------------------------------- 1 | # Reloads config.json 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" $HZN_EXCHANGE_URL/admin/initdb | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/admin/dropdb-token/root.sh: -------------------------------------------------------------------------------- 1 | # Gets all users as root 2 | source `dirname $0`/../../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" $HZN_EXCHANGE_URL/admin/dropdb/token | $parse -------------------------------------------------------------------------------- /src/test/bash/post/admin/dropdb/root.sh: -------------------------------------------------------------------------------- 1 | # Reloads config.json 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_RESET_TOKEN" $HZN_EXCHANGE_URL/admin/dropdb | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/admin/reload/root.sh: -------------------------------------------------------------------------------- 1 | # Reloads config.json 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" $EXCHANGE_URL_ROOT/v1/admin/reload | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/heartbeat/1.sh: -------------------------------------------------------------------------------- 1 | # Sends heartbeat from agbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" $EXCHANGE_URL_ROOT/v1/agbots/a1/heartbeat | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/reset/anonymous.sh: -------------------------------------------------------------------------------- 1 | # Requests a reset token email using no creds (allowed) 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/reset | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/nodes/notthere.sh: -------------------------------------------------------------------------------- 1 | # Tries to get a node that does not exist 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/nodes/9999 | $parse -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/user-urlparms.sh: -------------------------------------------------------------------------------- 1 | # Confirms the user/pw of a user 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/confirm'?password='$EXCHANGE_PW | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/agbots/notthere.sh: -------------------------------------------------------------------------------- 1 | # Tries to get an agbot that does not exist 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/agbots/a9999 | $parse -------------------------------------------------------------------------------- /src/test/bash/get/users/user-baduser.sh: -------------------------------------------------------------------------------- 1 | # Tries to get a user with bad user creds 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic baduser:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER | $parse -------------------------------------------------------------------------------- /config/exchange-api.tmpl: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "db": { 4 | "jdbcUrl": "$EXCHANGE_DB_URL", 5 | "user": "$EXCHANGE_DB_USER", 6 | "password": "$EXCHANGE_DB_PW" 7 | }, 8 | "root": { 9 | "password": "$EXCHANGE_ROOT_PW" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/heartbeat/1-user.sh: -------------------------------------------------------------------------------- 1 | # Sends heartbeat from agbot 1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/agbots/a1/heartbeat | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/nodes/heartbeat/1.sh: -------------------------------------------------------------------------------- 1 | # Sends heartbeat from node 1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" $EXCHANGE_URL_ROOT/v1/nodes/n1/heartbeat | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/user.sh: -------------------------------------------------------------------------------- 1 | # Confirms the user/pw of a user 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/admin/reload/user.sh: -------------------------------------------------------------------------------- 1 | # Tries to reload config.json as a user (not allowed) 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/admin/reload | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/nodes/heartbeat/1-user.sh: -------------------------------------------------------------------------------- 1 | # Sends heartbeat from node 1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/nodes/n1/heartbeat | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/user-urlparms2.sh: -------------------------------------------------------------------------------- 1 | # Confirms the user/pw of a user 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/confirm'?username='$EXCHANGE_USER'&password='$EXCHANGE_PW | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/get/users/all-root-encoded.sh: -------------------------------------------------------------------------------- 1 | # Confirms the user/pw of a user 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $(echo -n root/root:$EXCHANGE_ROOTPW | base64)" $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/2-create.sh: -------------------------------------------------------------------------------- 1 | # Adds user 2 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "2@gmail.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/2 | $parse 8 | -------------------------------------------------------------------------------- /src/main/resources/swagger/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | overflow: -moz-scrollbars-vertical; 4 | overflow-y: scroll; 5 | } 6 | 7 | *, 8 | *:before, 9 | *:after { 10 | box-sizing: inherit; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | background: #fafafa; 16 | } 17 | -------------------------------------------------------------------------------- /src/test/bash/get/agreements/agbots-notthere.sh: -------------------------------------------------------------------------------- 1 | # Tries to get an agreement of agbot a1 that does not exit 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/agbots/a1/agreements/9999 | $parse -------------------------------------------------------------------------------- /src/test/bash/get/agreements/nodes-notthere.sh: -------------------------------------------------------------------------------- 1 | # Tries to get an agreement of node 1 that does not exist 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X GET -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/nodes/1/agreements/9999 | $parse -------------------------------------------------------------------------------- /src/test/bash/post/users/2-create-badinput.sh: -------------------------------------------------------------------------------- 1 | # Adds user 2 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ 5 | "token": "'$EXCHANGE_PW'", 6 | "email": "2@gmail.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/2 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/dataheartbeat/123.sh: -------------------------------------------------------------------------------- 1 | # Updates the data received timestamp for agreement 123 of abbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" $EXCHANGE_URL_ROOT/v1/agbots/a1/dataheartbeat/123 | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/reset/user-root.sh: -------------------------------------------------------------------------------- 1 | # Requests a reset token email using using root creds 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/reset | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/reset/user.sh: -------------------------------------------------------------------------------- 1 | # Requests a reset token email using user creds (allowed) 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/reset | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/agreements/confirm/123.sh: -------------------------------------------------------------------------------- 1 | # Confirms agreement exists 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{"agreementId":"123"}' $EXCHANGE_URL_ROOT/v1/agreements/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/user-newpw.sh: -------------------------------------------------------------------------------- 1 | # Confirms the new pw of user (after changing it with a reset token) 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:newpw" $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/patch/agbots/1-agbot-token.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_AGBOTAUTH" -d '{ 5 | "token": "abcdef" 6 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/agreements/confirm/123-a2.sh: -------------------------------------------------------------------------------- 1 | # Confirms agreement exists 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a2:abcdef" -d '{"agreementId":"123"}' $EXCHANGE_URL_ROOT/v1/agreements/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/baduser.sh: -------------------------------------------------------------------------------- 1 | # Tries to confirm a user's pw using bad creds 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic 123456789fddasdfddsffdsdfdfssdf" $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/$EXCHANGE_USER/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/patch/nodes/1-node-publicKey-blank.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_NODEAUTH" -d '{ 5 | "publicKey": "" 6 | }' $EXCHANGE_URL_ROOT/v1/nodes/1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/patch/agbots/1-agbot-publicKey-blank.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_AGBOTAUTH" -d '{ 5 | "publicKey": "" 6 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/patch/nodes/1-node-badinput.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_NODEAUTH" -d '{ 5 | "bad": "rpi1-partial-update" 6 | }' $EXCHANGE_URL_ROOT/v1/nodes/1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/users/2-create-noemail.sh: -------------------------------------------------------------------------------- 1 | # Tries to add a user w/o an email address (invalid) 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "" 7 | }' $EXCHANGE_URL_ROOT/v1/users/2 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/agbots/1-agbot-badinput.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_AGBOTAUTH" -d '{ 5 | "bad": "agbot-partial-update" 6 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/patch/nodes/1-node-name.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "name": "rpi1-partial-update" 6 | }' $EXCHANGE_URL_ROOT/v1/nodes/1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/nodes/msgs/2-msg1.sh: -------------------------------------------------------------------------------- 1 | # Adds msg to node 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_AGBOTAUTH" -d '{ 5 | "message": "howdy", 6 | "ttl": 300 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/2/msgs | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/loglevel/root.sh: -------------------------------------------------------------------------------- 1 | # Changes the logging level 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "loggingLevel": "INFO" 6 | }' $EXCHANGE_URL_ROOT/v1/admin/loglevel | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/user2.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as user 2. Most useful for methods that do not need an input body, like GET and DELETE. 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | curl $copts -X $method -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/${EXCHANGE_USER}2:$EXCHANGE_PW" $EXCHANGE_URL_ROOT/v1/${org}$resource | $parse -------------------------------------------------------------------------------- /src/test/bash/patch/agbots/1-agbot-publicKey.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "publicKey": "newAGBOTABCDEF" 6 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/agreements/confirm/123-user.sh: -------------------------------------------------------------------------------- 1 | # Confirms this agreement exists 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{"agreementId":"123"}' $EXCHANGE_URL_ROOT/v1/agreements/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/users/confirm/user-encoded.sh: -------------------------------------------------------------------------------- 1 | # Confirms the user/pw of a user 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Accept: application/json' -H "Authorization:Basic $(echo -n $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW | base64)" $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/$EXCHANGE_USER/confirm | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/admin/hashpw/root.sh: -------------------------------------------------------------------------------- 1 | # Gets a hash of the specified pw 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_ROOTPW'" 6 | }' $EXCHANGE_URL_ROOT/v1/admin/hashpw | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/dataheartbeat/123-list.sh: -------------------------------------------------------------------------------- 1 | # Updates the data received timestamp for agreement 123 of abbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '["123"]' $EXCHANGE_URL_ROOT/v1/agbots/a1/dataheartbeat | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/nodes/msgs/1-msg1.sh: -------------------------------------------------------------------------------- 1 | # Adds msg to node 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_AGBOTAUTH" -d '{ 5 | "message": "hey there", 6 | "ttl": 300 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/n1/msgs | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/nodes/msgs/1-msg2.sh: -------------------------------------------------------------------------------- 1 | # Adds msg to node 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_AGBOTAUTH" -d '{ 5 | "message": "hello again", 6 | "ttl": 300 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/n1/msgs | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/nodes/1-node-publicKey.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" -d '{ 5 | "publicKey": "newABCDEF" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/msgs/a1-msg1.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 123 of agbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_NODEAUTH" -d '{ 5 | "message": "how do you do?", 6 | "ttl": 300 7 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1/msgs | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/users/user-create-anonymous.sh: -------------------------------------------------------------------------------- 1 | # Adds a user 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "admin": false, 7 | "email": "'$EXCHANGE_EMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/public/users/$EXCHANGE_USER | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/config/maxAgbots.sh: -------------------------------------------------------------------------------- 1 | # Sets 1 config value 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "varPath": "api.limits.maxAgbots", 6 | "value": "1" 7 | }' $EXCHANGE_URL_ROOT/v1/admin/config | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/config/maxDevices.sh: -------------------------------------------------------------------------------- 1 | # Sets 1 config value 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "varPath": "api.limits.maxnodes", 6 | "value": "1" 7 | }' $EXCHANGE_URL_ROOT/v1/admin/config | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/user-update-email.sh: -------------------------------------------------------------------------------- 1 | # Updates user 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "", 6 | "email": "brucemp7@gmail.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/nodes/1-node-pattern.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" -d '{ 5 | "pattern": "'$EXCHANGE_ORG'/p1x" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/config/maxAgreements.sh: -------------------------------------------------------------------------------- 1 | # Sets 1 config value 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "varPath": "api.limits.maxAgreements", 6 | "value": "1" 7 | }' $EXCHANGE_URL_ROOT/v1/admin/config | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/admin/hashpw/user.sh: -------------------------------------------------------------------------------- 1 | # Tries to get a hash of a pw as user (not allowed) 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_ROOTPW'" 6 | }' $EXCHANGE_URL_ROOT/v1/admin/hashpw | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/123-agbot.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 123 of agbot a1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "workload": "sdr-arm.json", 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1/agreements/123 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/456-agbot.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 456 of agbot a1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "workload": "sdr-arm.json", 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1/agreements/456 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/789-agbot.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 789 of agbot a1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "workload": "sdr-arm.json", 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1/agreements/789 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/2-update-root.sh: -------------------------------------------------------------------------------- 1 | # Updates the root email address 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "2-updated@gmail.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/2 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/user-update.sh: -------------------------------------------------------------------------------- 1 | # Updates user 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "'$EXCHANGE_EMAIL'" 7 | }' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/pattern/patch.sh: -------------------------------------------------------------------------------- 1 | # Updates a microservice 2 | source `dirname $0`/../../functions.sh PATCH $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "description": "this is now patched" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/orgs/create.sh: -------------------------------------------------------------------------------- 1 | # Adds a microservice 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "label": "International Business Machines Inc.", 6 | "description": "blah blah" 7 | }' $EXCHANGE_URL_ROOT/v1/orgs/IBM | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/config/maxMessagesInMailbox.sh: -------------------------------------------------------------------------------- 1 | # Sets 1 config value 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "varPath": "api.limits.maxMessagesInMailbox", 6 | "value": "1" 7 | }' $EXCHANGE_URL_ROOT/v1/admin/config | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/root-root.sh: -------------------------------------------------------------------------------- 1 | # Updates the root email address 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_ROOTPW'", 6 | "email": "somerealemail@root.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/root | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a1-compatible.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a1 to exchange 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot1", 7 | "msgEndPoint": "whisper-id" 8 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1 | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/put/users/2-create-root.sh: -------------------------------------------------------------------------------- 1 | # Adds user 2 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "2@gmail.com" 7 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/${EXCHANGE_USER}2 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/user-update-root.sh: -------------------------------------------------------------------------------- 1 | # Updates the root email address 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "'$EXCHANGE_EMAIL'" 7 | }' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/agbots/isrecentdata/123-list.sh: -------------------------------------------------------------------------------- 1 | # Updates the data received timestamp for agreement 123 of abbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{"secondsStale":300, "agreementIds":["123"]}' $EXCHANGE_URL_ROOT/v1/agbots/a1/isrecentdata | $parse 5 | -------------------------------------------------------------------------------- /src/test/bash/post/patterns/nodehealth/p1.sh: -------------------------------------------------------------------------------- 1 | # Adds a workload 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_AGBOTAUTH" -d '{ 5 | "lastTime": "2017-11-01T12:25:38.767Z[UTC]" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1/nodehealth | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/123-agbot-user.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 123 of agbot a1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "workload": "sdr-arm.json", 6 | "state": "finalized" 7 | }' $EXCHANGE_URL_ROOT/v1/agbots/a1/agreements/123 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/users/root-user.sh: -------------------------------------------------------------------------------- 1 | # Updates the root email address 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_ROOTPW'", 6 | "email": "thisshouldnotwork@root.com" 7 | }' $EXCHANGE_URL_ROOT/v1/users/root | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/services/gps-arch-bad.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "arch": "arm" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/patch/services/gps.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "sharable": "exclusive" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/put/users/user-create-root.sh: -------------------------------------------------------------------------------- 1 | # Updates the root user 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "email": "'$EXCHANGE_EMAIL'" 7 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/$EXCHANGE_USER | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/patch/services/gps-sharable-bad.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "sharable": "foobar" 6 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a3.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a3 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot3", 7 | "msgEndPoint": "whisper-id", 8 | "publicKey": "AGBOTDEF" 9 | }' $EXCHANGE_URL_ROOT/v1/agbots/a3 | $parse 10 | -------------------------------------------------------------------------------- /src/test/bash/post/users/user-create-root.sh: -------------------------------------------------------------------------------- 1 | # Adds a user 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "admin": true, 7 | "email": "'$EXCHANGE_EMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/$EXCHANGE_USER | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/README.md: -------------------------------------------------------------------------------- 1 | This directory and subdirs contain simple bash scripts running curl for ad hoc tests of the Exchagne API. 2 | For automated tests, see src/test/scala/exchangeapi 3 | 4 | ## Preconditions 5 | 6 | - Install jq (command-line JSON processor) 7 | - Set these environment variables: 8 | - EXCHANGE_USER 9 | - EXCHANGE_PW 10 | - EXCHANGE_ROOTPW 11 | - EXCHANGE_URL_ROOT: e.g. http://localhost:8080 -------------------------------------------------------------------------------- /src/test/bash/put/agreements/789-node.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 789 of node 1 as the node 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic 1:abc123" -d '{ 5 | "microservice": "https://bluehorizon.network/documentation/sdr-node-api", 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/1/agreements/789 | $parse 8 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/agentconfigurationmanagement/TestAgentConfigMgmt.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.agentconfigurationmanagement 2 | 3 | import org.scalatest.Suites 4 | 5 | // Run the following test suites sequentially, in order. 6 | class TestAgentConfigMgmt extends Suites ( 7 | new TestDeleteAgentConfigMgmt, 8 | new TestGetAgentConfigMgmt, 9 | new TestPutAgentConfigMgmt 10 | ) 11 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/123-node.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 123 of node 1 as the node 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic 1:abc123" -d '{ 5 | "microservices": ["https://bluehorizon.network/documentation/sdr-node-api"], 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/1/agreements/123 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/users/2-create-admin.sh: -------------------------------------------------------------------------------- 1 | # Adds a user 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "admin": false, 7 | "email": "'$EXCHANGE_EMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/${EXCHANGE_USER}2 | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a1-root.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a1 to exchange 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot1", 7 | "msgEndPoint": "", 8 | "publicKey": "AGBOTABC" 9 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/agbots/a1 | $parse 10 | -------------------------------------------------------------------------------- /src/test/bash/put/agreements/456-node.sh: -------------------------------------------------------------------------------- 1 | # Adds agreement 456 of node 2 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "microservice": "https://bluehorizon.network/documentation/netspeed-node-api", 6 | "state": "negotiating" 7 | }' $EXCHANGE_URL_ROOT/v1/nodes/1/agreements/456 | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/post/users/changepw/user.sh: -------------------------------------------------------------------------------- 1 | # Changes the user's pw using the reset token. Run like: EXCHANGE_RESET_TOKEN="" user.sh 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_RESET_TOKEN" -d '{ 5 | "newPassword": "newpw" 6 | }' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/changepw | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/post/users/user-create-frontend.sh: -------------------------------------------------------------------------------- 1 | # Updates the root user 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "type:person" -H "id:feuser" -H "orgid:$EXCHANGE_ORG" -H "issuer:IBM_ID" -d '{ 5 | "password": "notused", 6 | "admin": true, 7 | "email": "'$EXCHANGE_EMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/feuser | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/post/users/new-create-admin.sh: -------------------------------------------------------------------------------- 1 | # Adds a user 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_NEWPW'", 6 | "admin": false, 7 | "email": "'$EXCHANGE_NEWEMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/$EXCHANGE_NEWUSER | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a1.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a1 to exchange 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot1", 7 | "msgEndPoint": "", 8 | "publicKey": "AGBOTABC" 9 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/agbots/a1 | $parse 10 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a2-user.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a2 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot2", 7 | "msgEndPoint": "whisper-id", 8 | "publicKey": "AGBOTDEF" 9 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/agbots/a2 | $parse 10 | -------------------------------------------------------------------------------- /src/test/bash/post/users/changepw/user-backtoorig.sh: -------------------------------------------------------------------------------- 1 | # Changes the user's pw using the reset token. Run like: EXCHANGE_RESET_TOKEN="" user.sh 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_RESET_TOKEN" -d '{ 5 | "newPassword": "'$EXCHANGE_PW'" 6 | }' $EXCHANGE_URL_ROOT/v1/users/$EXCHANGE_USER/changepw | $parse 7 | -------------------------------------------------------------------------------- /src/test/bash/user.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as user. Most useful for methods that do not need an input body, like GET and DELETE. 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | #echo curl $copts -X $method -H 'Accept: application/json' -H "Authorization:Basic $HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" $HZN_EXCHANGE_URL/${org}$resource 5 | curl $copts -X $method -H "Authorization:Basic $HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" $HZN_EXCHANGE_URL/${org}$resource | $parse 6 | -------------------------------------------------------------------------------- /src/test/bash/post/services/dockauths/gps.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "registry": "registry.ng.bluemix.net", 6 | "token": "blahblah" 7 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64/dockauths | $parse 8 | -------------------------------------------------------------------------------- /src/test/bash/put/services/dockauths/gps-1.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "registry": "registry.ng.bluemix.net", 6 | "token": "blahblah1" 7 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64/dockauths/1 | $parse 8 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | %date [%thread] %-5level %logger{36} - %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/test/bash/post/patterns/search/location.sh: -------------------------------------------------------------------------------- 1 | # Adds a workload 2 | source `dirname $0`/../../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_AGBOTAUTH" -d '{ 5 | "serviceUrl": "https://bluehorizon.network/services/location", 6 | "secondsStale": 0, 7 | "startIndex": 0, 8 | "numEntries": 0 9 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1/search | $parse 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created/used by sbt 2 | /bin/ 3 | project/project/ 4 | target/ 5 | .lib/ 6 | 7 | # Create by Makefile to hold build timestamps 8 | /.docker-* 9 | 10 | /keys/ 11 | 12 | # IntelliJ IDEA 13 | .idea/ 14 | /exchange-api.iml 15 | /.bsp/ 16 | 17 | # VSCode 18 | /.bloop/ 19 | /.metals/ 20 | /.vscode/ 21 | /project/.bloop/ 22 | /project/metals.sbt 23 | 24 | # Eclipse? 25 | .classpath 26 | .cache-main 27 | .cache-tests 28 | .project 29 | .settings/ 30 | 31 | 32 | # Mac specific 33 | .DS_Store 34 | -------------------------------------------------------------------------------- /src/test/bash/post/users/3-create-2.sh: -------------------------------------------------------------------------------- 1 | # Tries to create a user - this should fail, because the creating user is not an admin 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/${EXCHANGE_USER}2:$EXCHANGE_PW" -d '{ 5 | "password": "'$EXCHANGE_PW'", 6 | "admin": false, 7 | "email": "'$EXCHANGE_EMAIL'" 8 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/users/${EXCHANGE_USER}3 | $parse 9 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a2-root.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a2 as root 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d '{ 5 | "token": "abcdef", 6 | "name": "agbot2-asroot", 7 | "patterns": [{ "orgid": "myorg", "pattern": "mypattern" }], 8 | "msgEndPoint": "whisper-id", 9 | "publicKey": "AGBOTDEF" 10 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/agbots/a2 | $parse 11 | -------------------------------------------------------------------------------- /src/test/bash/put/agbots/a1-frontend.sh: -------------------------------------------------------------------------------- 1 | # Adds agbot a1 to exchange 2 | source `dirname $0`/../../functions.sh GET $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "type:person" -H "id:feuser" -H "orgid:$EXCHANGE_ORG" -H "issuer:IBM_ID" -d '{ 5 | "token": "abcdef", 6 | "name": "fe-agbot", 7 | "patterns": [{ "orgid": "myorg", "pattern": "mypattern" }], 8 | "msgEndPoint": "whisper-id", 9 | "publicKey": "AGBOTABC" 10 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/agbots/feagbot | $parse 11 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/weird-user.sh: -------------------------------------------------------------------------------- 1 | # Updates node 1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abc123", 6 | "name": "rpi1", 7 | "pattern": "'$EXCHANGE_ORG'/mypat", 8 | "desiredServices": [], 9 | "msgEndPoint": "whisper-id", 10 | "softwareVersions": {}, 11 | "publicKey": "ABC" 12 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/my@node | $parse 13 | -------------------------------------------------------------------------------- /src/test/bash/patch/services/location.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PATCH -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" -d '{ 5 | "requiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/services/gps", 8 | "org": "'$HZN_ORG_ID'", 9 | "version": "[1.0.0,INFINITY)", 10 | "arch": "amd64" 11 | } 12 | ] 13 | }' $HZN_EXCHANGE_URL/orgs/$HZN_ORG_ID/services/bluehorizon.network-services-location_4.5.6_amd64 | $parse 14 | -------------------------------------------------------------------------------- /src/test/bash/https.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods as user via https. Most useful for methods that do not need an input body, like GET and DELETE. 2 | source `dirname $0`/functions.sh $1 $2 ${@:3} 3 | 4 | HZN_EXCHANGE_URL=https://edge-fab-exchange:8443/v1 5 | CERT_PEM=$(dirname $0)/../../../keys/etc/exchangecert.pem 6 | 7 | #echo curl $copts -X $method --cacert ../../../keys/etc/exchangecert.pem -H 'Accept: application/json' -H "Authorization:Basic $HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" $HZN_EXCHANGE_URL/${org}$resource 8 | curl $copts -X $method --cacert $CERT_PEM -H "Authorization:Basic $HZN_ORG_ID/$HZN_EXCHANGE_USER_AUTH" $HZN_EXCHANGE_URL/${org}$resource | $parse 9 | -------------------------------------------------------------------------------- /src/main/resources/swagger/swagger-initializer.js: -------------------------------------------------------------------------------- 1 | window.onload = function() { 2 | // 3 | 4 | // the following lines will be replaced by docker/configurator, when it runs in a docker-container 5 | window.ui = SwaggerUIBundle({ 6 | url: "https://petstore.swagger.io/v2/swagger.json", 7 | dom_id: '#swagger-ui', 8 | deepLinking: true, 9 | presets: [ 10 | SwaggerUIBundle.presets.apis, 11 | SwaggerUIStandalonePreset 12 | ], 13 | plugins: [ 14 | SwaggerUIBundle.plugins.DownloadUrl 15 | ], 16 | layout: "StandaloneLayout" 17 | }); 18 | 19 | // 20 | }; 21 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/PATCH.java: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi; 2 | 3 | // Needed because the version of javax.ws.rs we are forced to use does not have the PATCH annotation 4 | // Creation and use of this file is described at https://stackoverflow.com/questions/17897171/how-to-have-a-patch-annotation-for-jax-rs 5 | import javax.ws.rs.HttpMethod; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Target({ElementType.METHOD}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @HttpMethod("PATCH") 14 | public @interface PATCH { } 15 | -------------------------------------------------------------------------------- /tools/Dockerfile-render: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Do not need this, using the ARG statement in the dockerfile and --build-arg on docker build. 4 | 5 | # Replace the variables in the dockerfile template with their values from the environment 6 | 7 | if [ "$#" -lt 2 ]; then 8 | echo "Usage: $0 template_fname variables..." 9 | exit 1 10 | fi 11 | 12 | TEMPLATE=$1 13 | P="$(dirname $TEMPLATE)" 14 | FNAME="$(basename $TEMPLATE .tmpl)" 15 | DEST="$P/$FNAME" 16 | shift 17 | 18 | # cp "$TEMPLATE" "$DEST" 19 | 20 | # for v in ${@:2}; do 21 | for name in $*; do 22 | # replace the name of the variable with the value of the variable 23 | sed -i "s|##${name}##|${!name}|g" "$DEST" 24 | done 25 | -------------------------------------------------------------------------------- /src/test/bash/functions.sh: -------------------------------------------------------------------------------- 1 | # Meant to be sourced by the exchange manual test scripts 2 | 3 | method=$(echo $1 | awk '{print toupper($0)}') 4 | 5 | resource=${2#/} # remove leading slash in case there, because we will add it below 6 | if [[ ${resource:0:6} == "admin/" || ${resource:0:4} == "orgs" ]]; then 7 | org="" # the admin methods are not under the org resource 8 | else 9 | org="orgs/$HZN_ORG_ID/" 10 | fi 11 | 12 | if [[ -z $parse ]]; then 13 | if [[ $3 == "-r" ]]; then 14 | parse=cat 15 | copts='-sS -w %{http_code}' 16 | elif [[ $3 == "-rr" ]]; then 17 | parse=cat 18 | copts='-sS' 19 | else 20 | parse="jq ." 21 | copts='-sS -w %{http_code}' 22 | fi 23 | else 24 | copts='-s' 25 | fi -------------------------------------------------------------------------------- /src/test/bash/put/services/keys/key.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: text/plain' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '-----BEGIN CERTIFICATE----- 5 | MIII+jCCBOKgAwIBAgIUEfeMrmSFxCUKATcNPcowfs/lU9owDQYJKoZIhvcNAQEL 6 | BQAwJjEMMAoGA1UEChMDaWJtMRYwFAYDVQQDDA1icEB1cy5pYm0uY29tMB4XDTE4 7 | MDEwMjAxNDkyMFoXDTIyMDEwMjEzNDgzMFowJjEMMAoGA1UEChMDaWJtMRYwFAYD 8 | VQQDDA1icEB1cy5pYm0uY29tMIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKC 9 | -----END CERTIFICATE----- 10 | ' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64/keys/mykey3.pem | $parse 11 | -------------------------------------------------------------------------------- /src/test/bash/post/services/gps.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "GPS for amd64", 6 | "description": "blah blah", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/gps", 9 | "version": "1.2.3", 10 | "arch": "amd64", 11 | "sharable": "single", 12 | "deployment": "{\"services\":{\"gps\":{\"image\":\"summit.hovitos.engineering/x86/gps:1.2.3\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 13 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=" 14 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services | $parse 15 | -------------------------------------------------------------------------------- /src/test/bash/put/services/gps.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "updated sharable attribute", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/gps", 9 | "version": "1.2.3", 10 | "arch": "amd64", 11 | "sharable": "multiple", 12 | "deployment": "{\"services\":{\"gps\":{\"image\":\"summit.hovitos.engineering/x86/gps:1.2.3\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 13 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=" 14 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 15 | -------------------------------------------------------------------------------- /src/test/bash/put/services/gps-arch-bad.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "updated sharable attribute", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/gps", 9 | "version": "1.2.3", 10 | "arch": "arm", 11 | "sharable": "multiple", 12 | "deployment": "{\"services\":{\"gps\":{\"image\":\"summit.hovitos.engineering/x86/gps:1.2.3\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 13 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=" 14 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 15 | -------------------------------------------------------------------------------- /src/test/bash/put/services/gps-sharable-bad.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "updated sharable attribute", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/gps", 9 | "version": "1.2.3", 10 | "arch": "amd64", 11 | "sharable": "foobar", 12 | "deployment": "{\"services\":{\"gps\":{\"image\":\"summit.hovitos.engineering/x86/gps:1.2.3\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 13 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=" 14 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services/bluehorizon.network-services-gps_1.2.3_amd64 | $parse 15 | -------------------------------------------------------------------------------- /src/test/bash/frontend.sh: -------------------------------------------------------------------------------- 1 | # Run rest api methods from a simulated front end. Most useful for methods that do not need an input body, like GET and DELETE. 2 | source `dirname $0`/functions.sh $2 $3 ${@:4} 3 | 4 | # Data power calls us similar to: curl -u '{username}:{password}' 'https://{serviceURL}' -H 'type:{subjectType}' -H 'id:{username}' -H 'orgid:{org}' -H 'issuer:IBM_ID' -H 'Content-Type: application/json' 5 | # type: person (user logged into the dashboard), app (API Key), or dev (device/gateway) 6 | idType=$1 7 | if [[ $idType == "person" ]]; then 8 | id="feuser" 9 | elif [[ $idType == "app" ]]; then 10 | id="feapikey" 11 | elif [[ $idType == "dev" ]]; then 12 | id="fenode" 13 | else 14 | echo "Error: unknown front end id type: $idType" 15 | exit 2 16 | fi 17 | 18 | curl $copts -X $method -H 'Accept: application/json' -H "type:$idType" -H "id:$id" -H "orgid:$EXCHANGE_ORG" -H "issuer:IBM_ID" $EXCHANGE_URL_ROOT/v1/${org}$resource | $parse -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-noversion.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "wildcard", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | } 21 | ] 22 | } 23 | ], 24 | "secondsStale": 0, 25 | "propertiesToReturn": [ 26 | "string" 27 | ], 28 | "startIndex": 0, 29 | "numEntries": 0 30 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 31 | -------------------------------------------------------------------------------- /src/test/bash/put/admin/table/users.sh: -------------------------------------------------------------------------------- 1 | # Gets a hash of the specified pw 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | # Pipe the info into this 5 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root/root:$EXCHANGE_ROOTPW" -d @- $EXCHANGE_URL_ROOT/v1/admin/tables/users | $parse 6 | 7 | # curl -# -w "%{http_code}" -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '[ 8 | # { 9 | # "username": "2", 10 | # "password": "$2a$10$0wya.WH8ayVYJEoEBcM3g.SsjCmrVpR6tU.RFurYC9W3vK481nybC", 11 | # "email": "2@gmail.com", 12 | # "lastUpdated": "2016-12-02T13:12:25.030Z[UTC]" 13 | # }, 14 | # { 15 | # "username": "bp", 16 | # "password": "$2a$10$xNMVEfKCivXDr/d6pwuUCedRTt.puudl8PCSLjGuv32Ar4drafYW.", 17 | # "email": "bruceandml@gmail.com", 18 | # "lastUpdated": "2016-12-02T13:14:30.031Z[UTC]" 19 | # } 20 | # ]' $EXCHANGE_URL_ROOT/v1/admin/tables/users | $parse 21 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/status/1-node.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" -d '{ 5 | "connectivity": { 6 | "firmware.bluehorizon.network": true, 7 | "images.bluehorizon.network": true 8 | }, 9 | "services": [ 10 | { 11 | "agreementId": "78d7912aafb6c11b7a776f77d958519a6dc718b9bd3da36a1442ebb18fe9da30", 12 | "serviceUrl":"https://bluehorizon.network/workloads/location", 13 | "orgid":"ling.com", 14 | "version":"1.2", 15 | "arch":"amd64", 16 | "containers": [ 17 | { 18 | "name": "/dc23c045eb64e1637d027c4b0236512e89b2fddd3f06290c7b2354421d9d8e0d-location", 19 | "image": "summit.hovitos.engineering/x86/location:v1.2", 20 | "created": 1506086099, 21 | "state": "running" 22 | } 23 | ] 24 | } 25 | ] 26 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1/status | $parse 27 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/ExchConfigSuite.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | import org.junit.runner.RunWith 5 | import org.scalatestplus.junit.JUnitRunner 6 | import com.horizon.exchangeapi._ 7 | 8 | /** 9 | * Tests for the Version and VersionRange case classes 10 | */ 11 | @RunWith(classOf[JUnitRunner]) 12 | class ExchConfigSuite extends AnyFunSuite { 13 | test("ExchConfig tests") { 14 | ExchConfig.load() 15 | // Note: this test needs to work with the default version of config.json that is in src/main/resources (so that 'make test' in travis works) 16 | assert(ExchConfig.getInt("api.limits.maxNodes") === 45000) 17 | assert(ExchConfig.getInt("api.limits.maxAgreements") === 0) 18 | assert(ExchConfig.getInt("api.limits.maxMessagesInMailbox") === 0) 19 | assert(ExchConfig.getString("api.db.driverClass") === "org.postgresql.Driver") 20 | assert(ExchConfig.getInt("api.db.minPoolSize") === 1) 21 | assert(ExchConfig.getInt("api.db.acquireIncrement") === 1) 22 | assert(ExchConfig.getInt("api.db.maxPoolSize") === 50) 23 | } 24 | } -------------------------------------------------------------------------------- /src/test/bash/put/patterns/p1.sh: -------------------------------------------------------------------------------- 1 | # Adds a workload 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "My Pattern", "description": "blah blah", "public": true, 6 | "workloads": [ 7 | { 8 | "workloadUrl": "https://bluehorizon.network/workloads/netspeed", 9 | "workloadOrgid": "'$EXCHANGE_ORG'", 10 | "workloadArch": "amd64", 11 | "workloadVersions": [ 12 | { 13 | "version": "1.0.0", 14 | "deployment_overrides": "{\"services\":{\"location\":{\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 15 | "deployment_overrides_signature": "a", 16 | "priority": {}, 17 | "upgradePolicy": {} 18 | } 19 | ], 20 | "dataVerification": {}, 21 | "nodeHealth": { "missing_heartbeat_interval": 600, "check_agreement_status": 120 } 22 | } 23 | ], 24 | "agreementProtocols": [{ "name": "Basic" }] 25 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1 | $parse 26 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/netspeed-dave.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/netspeed-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "version", 17 | "value": "1.0.0", 18 | "propType": "version", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "agreementProtocols", 23 | "value": "Citizen Scientist", 24 | "propType": "list", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 0, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/netspeed-wildcard-agbot.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/netspeed-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "*", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 0, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-wildcard.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "wildcard", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 0, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-wildcard-prod.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "wildcard", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "Citizen Scientist", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 0, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/put/patterns/p1-no-nodehealth.sh: -------------------------------------------------------------------------------- 1 | # Adds a workload 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "My Pattern", "description": "blah blah", "public": true, 6 | "workloads": [ 7 | { 8 | "workloadUrl": "https://bluehorizon.network/workloads/netspeed", 9 | "workloadOrgid": "'$EXCHANGE_ORG'", 10 | "workloadArch": "amd64", 11 | "workloadVersions": [ 12 | { 13 | "version": "1.0.0", 14 | "deployment_overrides": "{\"services\":{\"location\":{\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 15 | "deployment_overrides_signature": "a", 16 | "priority": {}, 17 | "upgradePolicy": {} 18 | } 19 | ], 20 | "dataVerification": {} 21 | } 22 | ], 23 | "agreementProtocols": [{ "name": "Basic" }] 24 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1 | $parse 25 | 26 | # "nodeHealth": { "missing_heartbeat_interval": 600, "check_agreement_status": 120 } 27 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/netspeed-wildcard.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | # "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/netspeed-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "*", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 0, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-wildcard-stale.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "wildcard", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | } 29 | ], 30 | "secondsStale": 300, 31 | "propertiesToReturn": [ 32 | "string" 33 | ], 34 | "startIndex": 0, 35 | "numEntries": 0 36 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 37 | -------------------------------------------------------------------------------- /src/test/bash/post/patterns/p1.sh: -------------------------------------------------------------------------------- 1 | # Adds a pattern 2 | source `dirname $0`/../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "My Pattern", "description": "blah blah", "public": true, 6 | "services": [ 7 | { 8 | "serviceUrl": "https://bluehorizon.network/services/netspeed", 9 | "serviceOrgid": "'$EXCHANGE_ORG'", 10 | "serviceArch": "amd64", 11 | "serviceVersions": [ 12 | { 13 | "version": "1.0.0", 14 | "deployment_overrides": "{\"services\":{\"location\":{\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 15 | "deployment_overrides_signature": "a", 16 | "priority": {}, 17 | "upgradePolicy": {} 18 | } 19 | ], 20 | "dataVerification": {}, 21 | "nodeHealth": { 22 | "missing_heartbeat_interval": 600, 23 | "check_agreement_status": 120 24 | } 25 | } 26 | ], 27 | "agreementProtocols": [{ "name": "Basic" }] 28 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/patterns/p1 | $parse 29 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Builds docker image of our exchange svr 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "[1.8.1,)") 3 | 4 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "[5.2.4,)") 5 | 6 | // Linter 7 | //addSbtPlugin("com.sksamuel.scapegoat" %% "sbt-scapegoat" % "[1.1.0,)") 8 | 9 | // A fast restart of our rest api svr in sbt. Does NOT require use of spray 10 | addSbtPlugin("io.spray" % "sbt-revolver" % "[0.9.1,)") 11 | 12 | // To see the current versions being used, uncomment this line, then run: sbt dependencyTree 13 | // addSbtPlugin("net.virtual-void" % "sbt-dependencpwdy-graph" % "[0.8.0,)") 14 | 15 | // Reformats the scala source code when compiling it - this was giving parser errors w/o giving line numbers 16 | // addSbtPlugin("org.scalariform" % "sbt-scalariform" % "[1.8.2,)") 17 | 18 | // addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "latest.release") 19 | 20 | // Code coverage report generation 21 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "[1.6.1,)") 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/test/bash/scale/summarize.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Reduce/summarize the node output files from scaledriver.sh 4 | 5 | if [[ "$1" == "-h" ]]; then 6 | echo "Usage: $(basename $0)" 7 | exit 1 8 | fi 9 | 10 | # default of where to write the summary or error msgs. Can be overridden 11 | EX_PERF_REPORT_DIR="${EX_PERF_REPORT_DIR:-/tmp/exchangePerf}" 12 | 13 | dir=`dirname $0` 14 | 15 | function checkexitcode { 16 | if [[ $1 == 0 ]]; then return; fi 17 | # write error msg to both the summary file and stderr 18 | echo "Summarize:===============> command $2 failed with exit code $1, exiting." 19 | exit 3 20 | #if [[ "$3" != 'continue' ]]; then exit $1; fi 21 | } 22 | 23 | #cd $EX_PERF_REPORT_DIR 24 | 25 | # Show the agbot full summary files 26 | head -n 100 $EX_PERF_REPORT_DIR/*/agbot/*.summary 27 | checkexitcode $? "head -n 100 $EX_PERF_REPORT_DIR/*/agbot/*.summary" 28 | 29 | # Show just the critical lines from the node summaries, grouped by host 30 | for d in $EX_PERF_REPORT_DIR/*/node; do 31 | echo '' 32 | grep -v -E "^(Simulated |Start time: )" $d/*.summary 33 | checkexitcode $? "grep -v -E \"^(Simulated |Start time: )\" $d/*.summary" 34 | done 35 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-badpw.sh: -------------------------------------------------------------------------------- 1 | # Searches for nodes using bad creds 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:badpw" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "1.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "dataVerification", 29 | "value": "true", 30 | "propType": "boolean", 31 | "op": "=" 32 | } 33 | ] 34 | } 35 | ], 36 | "secondsStale": 432000, 37 | "propertiesToReturn": [ 38 | "string" 39 | ], 40 | "startIndex": 0, 41 | "numEntries": 0 42 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 43 | -------------------------------------------------------------------------------- /src/test/go/Makefile: -------------------------------------------------------------------------------- 1 | # Build the performance/scale test drivers for the exchange 2 | 3 | # To cross-compile for linux from mac: GOOS=linux make $GOOS/node 4 | OS := $(shell uname) 5 | ifeq ($(OS),Darwin) 6 | # Mac OS X 7 | export GOOS ?= darwin 8 | else 9 | # Assume Linux (could test by test if OS is Linux) 10 | export GOOS ?= linux 11 | endif 12 | 13 | all: darwin/node linux/node darwin/agbot linux/agbot 14 | 15 | darwin/node: node/node.go perfutils/perfutils.go 16 | mkdir -p $(shell dirname $@) 17 | GOOS=$(shell dirname $@) go build -o $@ $< 18 | 19 | linux/node: node/node.go perfutils/perfutils.go 20 | mkdir -p $(shell dirname $@) 21 | GOOS=$(shell dirname $@) go build -o $@ $< 22 | 23 | darwin/agbot: agbot/agbot.go perfutils/perfutils.go 24 | mkdir -p $(shell dirname $@) 25 | GOOS=$(shell dirname $@) go build -o $@ $< 26 | 27 | linux/agbot: agbot/agbot.go perfutils/perfutils.go 28 | mkdir -p $(shell dirname $@) 29 | GOOS=$(shell dirname $@) go build -o $@ $< 30 | 31 | $(GOOS)/smalltest: smalltest/smalltest.go 32 | @echo GOOS=$(GOOS) 33 | mkdir -p $(GOOS) 34 | go build -o $@ $< 35 | 36 | testnode: $(GOOS)/node 37 | ../bash/scale/deleteperforg.sh 38 | $< 1 39 | 40 | testagbot: $(GOOS)/agbot 41 | ../bash/scale/deleteperforg.sh 42 | $< 1 43 | -------------------------------------------------------------------------------- /src/test/bash/post/services/location.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "blah blah", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/location", 9 | "version": "4.5.6", 10 | "arch": "amd64", 11 | "sharable": "multiple", 12 | "matchHardware": {}, 13 | "requiredServices": [ 14 | { 15 | "url": "https://bluehorizon.network/services/gps", 16 | "org": "IBM", 17 | "version": "[1.0.0,INFINITY)", 18 | "arch": "amd64" 19 | } 20 | ], 21 | "userInput": [ 22 | { 23 | "name": "foo", 24 | "label": "The Foo Value", 25 | "type": "string", 26 | "defaultValue": "bar" 27 | } 28 | ], 29 | "deployment": "{\"services\":{\"location\":{\"image\":\"summit.hovitos.engineering/x86/location:4.5.6\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 30 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=", 31 | "pkg": { 32 | "storeType": "dockerRegistry" 33 | } 34 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services | $parse 35 | -------------------------------------------------------------------------------- /src/test/bash/post/services/location-bad-reqsvc.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "blah blah", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/location", 9 | "version": "4.5.6", 10 | "arch": "amd64", 11 | "sharable": "single", 12 | "matchHardware": {}, 13 | "requiredServices": [ 14 | { 15 | "url": "https://bluehorizon.network/services/foo", 16 | "org": "IBM", 17 | "version": "[1.0.0,INFINITY)", 18 | "arch": "amd64" 19 | } 20 | ], 21 | "userInput": [ 22 | { 23 | "name": "foo", 24 | "label": "The Foo Value", 25 | "type": "string", 26 | "defaultValue": "bar" 27 | } 28 | ], 29 | "deployment": "{\"services\":{\"location\":{\"image\":\"summit.hovitos.engineering/x86/location:4.5.6\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 30 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=", 31 | "pkg": { 32 | "storeType": "dockerRegistry" 33 | } 34 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services | $parse 35 | -------------------------------------------------------------------------------- /src/test/bash/post/services/location-diff-arch-bad.sh: -------------------------------------------------------------------------------- 1 | # Adds a service 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "label": "Location for amd64", 6 | "description": "blah blah", 7 | "public": true, 8 | "url": "https://bluehorizon.network/services/location", 9 | "version": "4.5.6", 10 | "arch": "arm", 11 | "sharable": "single", 12 | "matchHardware": {}, 13 | "requiredServices": [ 14 | { 15 | "url": "https://bluehorizon.network/services/gps", 16 | "org": "IBM", 17 | "version": "[1.0.0,INFINITY)", 18 | "arch": "amd64" 19 | } 20 | ], 21 | "userInput": [ 22 | { 23 | "name": "foo", 24 | "label": "The Foo Value", 25 | "type": "string", 26 | "defaultValue": "bar" 27 | } 28 | ], 29 | "deployment": "{\"services\":{\"location\":{\"image\":\"summit.hovitos.engineering/x86/location:4.5.6\",\"environment\":[\"USE_NEW_STAGING_URL=false\"]}}}", 30 | "deploymentSignature": "EURzSkDyk66qE6esYUDkLWLzM=", 31 | "pkg": { 32 | "storeType": "dockerRegistry" 33 | } 34 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/services | $parse 35 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/4-badop.sh: -------------------------------------------------------------------------------- 1 | # Tries to add a node with an invalid property op 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "a", 6 | "name": "rpi4", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi4 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "=" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "dataVerification", 33 | "value": "true", 34 | "propType": "boolean", 35 | "op": "=" 36 | } 37 | ] 38 | } 39 | ], 40 | "msgEndPoint": "whisper-id", 41 | "softwareVersions": {}, 42 | "publicKey": "DEF" 43 | }' $EXCHANGE_URL_ROOT/v1/nodes/4 | $parse 44 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/999-badinput.sh: -------------------------------------------------------------------------------- 1 | # Updates node 1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abc123", 6 | "foobar": "rpi999", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi1 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "dataVerification", 33 | "value": "true", 34 | "propType": "boolean", 35 | "op": "=" 36 | } 37 | ] 38 | } 39 | ], 40 | "msgEndPoint": "whisper-id", 41 | "softwareVersions": {"horizon": "3.2.1"}, 42 | "publicKey": "DEF" 43 | }' $EXCHANGE_URL_ROOT/v1/nodes/999 | $parse 44 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/4-badbool.sh: -------------------------------------------------------------------------------- 1 | # Tries to add a node with an invalid boolean property type 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "a", 6 | "name": "rpi4", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi4 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "dataVerification", 33 | "value": "truex", 34 | "propType": "boolean", 35 | "op": "=" 36 | } 37 | ] 38 | } 39 | ], 40 | "msgEndPoint": "whisper-id", 41 | "softwareVersions": {}, 42 | "publicKey": "DEF" 43 | }' $EXCHANGE_URL_ROOT/v1/nodes/4 | $parse 44 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/4-badint.sh: -------------------------------------------------------------------------------- 1 | # Tries to add a node with an invalid integer property type 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "a", 6 | "name": "rpi4", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi4 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400MB", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "dataVerification", 33 | "value": "true", 34 | "propType": "boolean", 35 | "op": "=" 36 | } 37 | ] 38 | } 39 | ], 40 | "msgEndPoint": "whisper-id", 41 | "softwareVersions": {}, 42 | "publicKey": "DEF" 43 | }' $EXCHANGE_URL_ROOT/v1/nodes/4 | $parse 44 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/tables/ExchangePostgresProfile.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.tables 2 | 3 | import org.json4s._ 4 | import com.github.tminglei.slickpg._ 5 | import org.json4s.native.Document 6 | import slick.jdbc.PostgresProfile 7 | 8 | trait ExchangePostgresProfile extends PostgresProfile 9 | with PgJson4sSupport 10 | with array.PgArrayJdbcTypes { 11 | /// for json support 12 | override val pgjson = "jsonb" 13 | type DOCType = org.json4s.native.Document 14 | override val jsonMethods: JsonMethods[Document] = org.json4s.native.JsonMethods.asInstanceOf[JsonMethods[DOCType]] 15 | 16 | override val api: APIExchange = new APIExchange {} 17 | 18 | val plainAPI = new APIExchange with Json4sJsonPlainImplicits 19 | 20 | /// 21 | trait APIExchange extends super.API with JsonImplicits { 22 | implicit val strListTypeMapper: DriverJdbcType[List[String]] = new SimpleArrayJdbcType[String]("text").to(_.toList) 23 | implicit val json4sJsonArrayTypeMapper: DriverJdbcType[List[JValue]] = 24 | new AdvancedArrayJdbcType[JValue](pgjson, 25 | (s) => utils.SimpleArrayUtils.fromString[JValue](jsonMethods.parse(_))(s).orNull, 26 | (v) => utils.SimpleArrayUtils.mkString[JValue](j=>jsonMethods.compact(jsonMethods.render(j)))(v) 27 | ).to(_.toList) 28 | } 29 | } 30 | object ExchangePostgresProfile extends ExchangePostgresProfile -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-arch-x86.sh: -------------------------------------------------------------------------------- 1 | # Searches for only nodes that have arch=x86 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "x86", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "1.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "true", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-good-agbot.sh: -------------------------------------------------------------------------------- 1 | # Searches for nodes that should be in the exchange as agbot a1 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic a1:abcdef" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "1.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "true", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 49 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-memory-400.sh: -------------------------------------------------------------------------------- 1 | # Searches for only nodes that have memory>=400 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "400", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "1.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "true", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 49 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-version-2.sh: -------------------------------------------------------------------------------- 1 | # Searches for only nodes that have version=2.0.0 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "2.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "true", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 49 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-datav-false.sh: -------------------------------------------------------------------------------- 1 | # Searches for only nodes that have dataVerification=false 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "1.0.0", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "false", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 49 | -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/sdr-good.sh: -------------------------------------------------------------------------------- 1 | # Searches for nodes that should be in the exchange as user 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "arm", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "memory", 17 | "value": "300", 18 | "propType": "int", 19 | "op": ">=" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "[1.0.0,2.0.0]", 24 | "propType": "version", 25 | "op": "in" 26 | }, 27 | { 28 | "name": "agreementProtocols", 29 | "value": "ExchangeManualTest", 30 | "propType": "list", 31 | "op": "in" 32 | }, 33 | { 34 | "name": "dataVerification", 35 | "value": "true", 36 | "propType": "boolean", 37 | "op": "=" 38 | } 39 | ] 40 | } 41 | ], 42 | "secondsStale": 0, 43 | "propertiesToReturn": [ 44 | "string" 45 | ], 46 | "startIndex": 0, 47 | "numEntries": 0 48 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 49 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/2-memory-400-node.sh: -------------------------------------------------------------------------------- 1 | # Adds node 2 as the node 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic 2:abcdef" -d '{ 5 | "token": "abcdef", 6 | "name": "rpi2-node", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi2 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | } 45 | ], 46 | "msgEndPoint": "whisper-id", 47 | "softwareVersions": {}, 48 | "publicKey": "DEF" 49 | }' $EXCHANGE_URL_ROOT/v1/nodes/2 | $parse 50 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/2-memory-400.sh: -------------------------------------------------------------------------------- 1 | # Adds node 2 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abcdef", 6 | "name": "rpi2", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi2 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "400", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | } 45 | ], 46 | "msgEndPoint": "whisper-id", 47 | "softwareVersions": {}, 48 | "publicKey": "DEF" 49 | }' $EXCHANGE_URL_ROOT/v1/nodes/2 | $parse 50 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/3-version-2-user.sh: -------------------------------------------------------------------------------- 1 | # Adds node 3 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "def", 6 | "name": "rpi3", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi3 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "300", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "2.0.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | } 45 | ], 46 | "msgEndPoint": "whisper-id", 47 | "softwareVersions": {}, 48 | "publicKey": "DEF" 49 | }' $EXCHANGE_URL_ROOT/v1/nodes/3 | $parse 50 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/3-version-2-root.sh: -------------------------------------------------------------------------------- 1 | # Adds node 3 as root 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic root:$EXCHANGE_ROOTPW" -d '{ 5 | "token": "def", 6 | "name": "rpi3-from-root", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi3 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "300", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "2.0.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | } 45 | ], 46 | "msgEndPoint": "whisper-id", 47 | "softwareVersions": {}, 48 | "publicKey": "DEF" 49 | }' $EXCHANGE_URL_ROOT/v1/nodes/3 | $parse 50 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/1-user.sh: -------------------------------------------------------------------------------- 1 | # Updates node 1 2 | source `dirname $0`/../../functions.sh '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abc123", 6 | "name": "rpi1", 7 | "pattern": "'$EXCHANGE_ORG'/p1", 8 | "registeredServices": [ 9 | { 10 | "url": "https://bluehorizon.network/services/sdr", 11 | "numAgreements": 1, 12 | "policy": "{json policy for rpi1 sdr}", 13 | "properties": [ 14 | { 15 | "name": "arch", 16 | "value": "arm", 17 | "propType": "string", 18 | "op": "in" 19 | }, 20 | { 21 | "name": "version", 22 | "value": "1.0", 23 | "propType": "version", 24 | "op": "in" 25 | } 26 | ] 27 | }, 28 | { 29 | "url": "https://bluehorizon.network/services/netspeed", 30 | "numAgreements": 1, 31 | "policy": "{json policy for rpi1 netspeed}", 32 | "properties": [ 33 | { 34 | "name": "arch", 35 | "value": "arm", 36 | "propType": "string", 37 | "op": "in" 38 | }, 39 | { 40 | "name": "version", 41 | "value": "1.0.0", 42 | "propType": "version", 43 | "op": "in" 44 | } 45 | ] 46 | } 47 | ], 48 | "msgEndPoint": "whisper-id", 49 | "softwareVersions": {"horizon": "3.2.1"}, 50 | "publicKey": "ABC" 51 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1 | $parse 52 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/1-node.sh: -------------------------------------------------------------------------------- 1 | # Update node 1 as node 2 | source `dirname $0`/../../functions.sh PUT '' '' $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_NODEAUTH" -d '{ 5 | "token": "abc123", 6 | "name": "rpi1-updated", 7 | "pattern": "'$EXCHANGE_ORG'/p1", 8 | "registeredServices": [ 9 | { 10 | "url": "https://bluehorizon.network/services/sdr", 11 | "numAgreements": 1, 12 | "policy": "{updated json policy for rpi1 sdr}", 13 | "properties": [ 14 | { 15 | "name": "arch", 16 | "value": "arm", 17 | "propType": "string", 18 | "op": "in" 19 | }, 20 | { 21 | "name": "version", 22 | "value": "1.0", 23 | "propType": "version", 24 | "op": "in" 25 | } 26 | ] 27 | }, 28 | { 29 | "url": "https://bluehorizon.network/services/netspeed", 30 | "numAgreements": 1, 31 | "policy": "{json policy for rpi1 netspeed}", 32 | "properties": [ 33 | { 34 | "name": "arch", 35 | "value": "arm", 36 | "propType": "string", 37 | "op": "in" 38 | }, 39 | { 40 | "name": "version", 41 | "value": "1.0.0", 42 | "propType": "version", 43 | "op": "in" 44 | } 45 | ] 46 | } 47 | ], 48 | "msgEndPoint": "whisper-id", 49 | "softwareVersions": {"horizon": "1.2.1"}, 50 | "publicKey": "ABC" 51 | }' $EXCHANGE_URL_ROOT/v1/orgs/$EXCHANGE_ORG/nodes/n1 | $parse 52 | -------------------------------------------------------------------------------- /.github/workflows/exchange-build.yml: -------------------------------------------------------------------------------- 1 | name: exchange-unit-testing 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 9 | jobs: 10 | # This workflow contains a single job called "build" 11 | build: 12 | # The type of runner that the job will run on 13 | runs-on: ubuntu-latest 14 | env: 15 | GOPATH: /home/runner/work/anax/anax/go 16 | DEBIAN_FRONTEND: noninteractive 17 | DOCKER_NETWORK: exchange-api-network 18 | DOCKER_REGISTRY: openhorizon 19 | EXCHANGE_FE_HEADER: issuer 20 | EXCHANGE_ROOTPW: ci-password 21 | POSTGRES_DB_NAME: exchange 22 | POSTGRES_DB_PORT: 5432 23 | POSTGRES_DB_USER: admin 24 | 25 | # Steps represent a sequence of tasks that will be executed as part of the job 26 | steps: 27 | - uses: actions/checkout@v2 28 | - uses: olafurpg/setup-scala@v10 29 | with: 30 | java-version: openjdk@1.17 31 | 32 | - name: Create Docker Env 33 | run: | 34 | pwd 35 | java -version 36 | make docker-network 37 | docker run -d -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=$POSTGRES_DB_NAME -e POSTGRES_USER=$POSTGRES_DB_USER --network $DOCKER_NETWORK --name postgres postgres 38 | export POSTGRES_CONTAINER_ADDRESS=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres) 39 | make run-docker 40 | cat /etc/horizon/exchange/config-http.json 41 | docker ps -a 42 | docker network ls 43 | make test -------------------------------------------------------------------------------- /src/test/bash/post/search/devices/multiple-wildcard.sh: -------------------------------------------------------------------------------- 1 | # Uses wildcars to searches for all nodes in the exchange 2 | source `dirname $0`/../../../functions.sh POST $* 3 | 4 | curl $copts -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "desiredServices": [ 6 | { 7 | "url": "https://bluehorizon.network/documentation/netspeed-node-api", 8 | "properties": [ 9 | { 10 | "name": "arch", 11 | "value": "*", 12 | "propType": "string", 13 | "op": "in" 14 | }, 15 | { 16 | "name": "agreementProtocols", 17 | "value": "ExchangeManualTest", 18 | "propType": "list", 19 | "op": "in" 20 | }, 21 | { 22 | "name": "version", 23 | "value": "*", 24 | "propType": "version", 25 | "op": "in" 26 | } 27 | ] 28 | }, 29 | { 30 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 31 | "properties": [ 32 | { 33 | "name": "arch", 34 | "value": "arm", 35 | "propType": "wildcard", 36 | "op": "in" 37 | }, 38 | { 39 | "name": "agreementProtocols", 40 | "value": "ExchangeManualTest", 41 | "propType": "list", 42 | "op": "in" 43 | }, 44 | { 45 | "name": "version", 46 | "value": "*", 47 | "propType": "version", 48 | "op": "in" 49 | } 50 | ] 51 | } 52 | ], 53 | "secondsStale": 0, 54 | "propertiesToReturn": [ 55 | "string" 56 | ], 57 | "startIndex": 0, 58 | "numEntries": 0 59 | }' $EXCHANGE_URL_ROOT/v1/search/nodes | $parse 60 | -------------------------------------------------------------------------------- /src/test/bash/scale/deleteperforg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Completely cleans up after a node/agbot performance test run by deleting the org used for that. 4 | # This can not be done inside the scripts running the perf test, because if multiple instances are run on multiple machines they can't know when 5 | # they are all done with the org. 6 | 7 | #if [[ -z $1 ]]; then 8 | # echo "Usage: $0 " 9 | # exit 1 10 | #fi 11 | #perfOrg=$1 12 | 13 | # These env vars are required 14 | if [[ -z "$EXCHANGE_ROOTPW" || -z "$HZN_EXCHANGE_URL" ]]; then 15 | echo "Error: environment variables EXCHANGE_ROOTPW and HZN_EXCHANGE_URL must be set." 16 | exit 1 17 | fi 18 | 19 | EX_PERF_ORG="${EX_PERF_ORG:-performancenodeagbot}" 20 | url="orgs/$EX_PERF_ORG" 21 | rootauth="root/root:$EXCHANGE_ROOTPW" 22 | auth="$rootauth" 23 | #echo "Running DELETE $url ..." 24 | #auth="-H Authorization:Basic$auth" # no spaces so we do not need to quote it 25 | auth="-u $auth" # no spaces so we do not need to quote it 26 | 27 | 28 | if [[ -n "$EX_PERF_CERT_FILE" ]]; then 29 | certFile="--cacert $EX_PERF_CERT_FILE" 30 | else 31 | certFile="-k" 32 | fi 33 | 34 | curlBasicArgs="-sS -w %{http_code} --output /dev/null $certFile" 35 | 36 | echo curl -X DELETE $curlBasicArgs $auth $HZN_EXCHANGE_URL/$url 37 | rc=$(curl -X DELETE $curlBasicArgs $auth $HZN_EXCHANGE_URL/$url) 38 | 39 | if [[ "$rc" == 204 ]]; then 40 | exit # everything is good 41 | elif [[ "$rc" == 404 ]]; then 42 | echo "$url does not exist" 43 | exit # still return a 0 exit code, because it is gone like they want it to be 44 | else 45 | httpcode="${rc:0:3}" # when an error occurs with curl the rest method output comes in stderr with the http code 46 | echo "Error: DELETE $url failed with: $rc, exiting." 47 | exit $httpcode 48 | fi 49 | 50 | -------------------------------------------------------------------------------- /src/main/resources/swagger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/test/bash/scale/wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Facilitates scale testing of Exchange. Runs the specified number of instances of test.sh in the background and waits for them to finish. 4 | # For true scale testing use a higher level script like scaledriver.sh to run wrapper.sh on multiple systems, giving each one a different namebase. 5 | 6 | if [[ -z $3 ]]; then 7 | echo "Usage: $0 ..." 8 | exit 1 9 | fi 10 | 11 | namebase=$1 12 | 13 | # default of where to write the summary or error msgs. Can be overridden 14 | EX_PERF_REPORT_DIR="${EX_PERF_REPORT_DIR:-/tmp/exchangePerf}" 15 | 16 | dir=`dirname $0` 17 | 18 | #function checkrc { 19 | # if [[ $1 != $2 ]]; then 20 | # echo "Error: Cmd failed with rc $1" 21 | # exit $1 22 | # fi 23 | #} 24 | 25 | # Clean up our children 26 | trapHandler() { 27 | kill $pids 28 | exit 29 | } 30 | 31 | # Clear out all of the summaries (in case we are running with a lower number or different script than previous) 32 | #for d in ${@:2}; do 33 | # # some of these args are numbers, but the -f flag will ignore those cases silently 34 | # rm -rf $EX_PERF_REPORT_DIR/$d/* 35 | #done 36 | rm -rf $EX_PERF_REPORT_DIR/* 37 | 38 | # Loop thru arg pairs (this 1st shift gets rid of namebase) 39 | while shift; do 40 | if [[ -z "$1" ]]; then break; fi # we are done 41 | 42 | # Get the next pair of args 43 | numInstances="$1" 44 | shift 45 | if [[ -z "$1" ]]; then 46 | echo "Error: no script specified after '$numInstances'" 47 | exit 1 48 | fi 49 | script="$1" 50 | 51 | # Fork the specified number of instances of this script 52 | for (( i=1 ; i<=$numInstances ; i++ )) ; do 53 | $dir/$script "${namebase}-$i" "$namebase" & 54 | pids="$pids $!" 55 | done 56 | done 57 | 58 | trap trapHandler SIGINT 59 | 60 | # I think by specifying the list of pids, wait will return a non-zero exit code if any of the children do 61 | wait $pids 62 | exit $? 63 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/1-user-compatible.sh: -------------------------------------------------------------------------------- 1 | # Updates node 1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abc123", 6 | "name": "rpi1", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi1 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "memory", 21 | "value": "300", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | }, 45 | { 46 | "url": "https://bluehorizon.network/documentation/netspeed-node-api", 47 | "numAgreements": 1, 48 | "policy": "{json policy for rpi1 netspeed}", 49 | "properties": [ 50 | { 51 | "name": "arch", 52 | "value": "arm", 53 | "propType": "string", 54 | "op": "in" 55 | }, 56 | { 57 | "name": "version", 58 | "value": "1.0.0", 59 | "propType": "version", 60 | "op": "in" 61 | } 62 | ] 63 | } 64 | ], 65 | "msgEndPoint": "whisper-id", 66 | "softwareVersions": {"horizon": "3.2.1"} 67 | }' $EXCHANGE_URL_ROOT/v1/nodes/n1 | $parse 68 | -------------------------------------------------------------------------------- /src/test/bash/put/nodes/1-user-changes.sh: -------------------------------------------------------------------------------- 1 | # Updates node 1 2 | source `dirname $0`/../../functions.sh PUT $* 3 | 4 | curl $copts -X PUT -H 'Content-Type: application/json' -H 'Accept: application/json' -H "Authorization:Basic $EXCHANGE_ORG/$EXCHANGE_USER:$EXCHANGE_PW" -d '{ 5 | "token": "abc123", 6 | "name": "rpi1-modified-micros", 7 | "desiredServices": [ 8 | { 9 | "url": "https://bluehorizon.network/documentation/sdr-node-api", 10 | "numAgreements": 1, 11 | "policy": "{json policy for rpi1 sdr}", 12 | "properties": [ 13 | { 14 | "name": "arch", 15 | "value": "arm", 16 | "propType": "string", 17 | "op": "in" 18 | }, 19 | { 20 | "name": "cpus", 21 | "value": "2", 22 | "propType": "int", 23 | "op": ">=" 24 | }, 25 | { 26 | "name": "version", 27 | "value": "1.0", 28 | "propType": "version", 29 | "op": "in" 30 | }, 31 | { 32 | "name": "agreementProtocols", 33 | "value": "ExchangeManualTest", 34 | "propType": "list", 35 | "op": "in" 36 | }, 37 | { 38 | "name": "dataVerification", 39 | "value": "true", 40 | "propType": "boolean", 41 | "op": "=" 42 | } 43 | ] 44 | }, 45 | { 46 | "url": "https://bluehorizon.network/documentation/netspeed222-node-api", 47 | "numAgreements": 1, 48 | "policy": "{json policy for rpi1 netspeed}", 49 | "properties": [ 50 | { 51 | "name": "arch", 52 | "value": "arm", 53 | "propType": "string", 54 | "op": "in" 55 | }, 56 | { 57 | "name": "version", 58 | "value": "1.0.0", 59 | "propType": "version", 60 | "op": "in" 61 | } 62 | ] 63 | } 64 | ], 65 | "msgEndPoint": "whisper-id", 66 | "softwareVersions": {"horizon": "3.2.1"} 67 | }' $EXCHANGE_URL_ROOT/v1/nodes/n1 | $parse 68 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/SlickSuite.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | //someday: use : http://blog.abhinav.ca/blog/2014/09/08/unit-testing-futures-with-scalatest/ to add slick unit tests 8 | 9 | /** 10 | * Tests for Slick DB table 11 | * to run: `test-only exchangeapi.SlickSuite` 12 | * clear and detailed tutorial of FunSuite: http://doc.scalatest.org/1.9.1/index.html#org.scalatest.FunSuite 13 | */ 14 | @RunWith(classOf[JUnitRunner]) 15 | class SlickSuite extends AnyFunSuite { 16 | 17 | ignore("listUsers") { 18 | // val cpds = new ComboPooledDataSource 19 | // val db = Database.forDataSource(cpds) 20 | // db.run(UsersTQ.getUser("bob").result) map { xs => 21 | // println(xs) 22 | // } 23 | } 24 | ignore("get user password") { 25 | // val cpds = new ComboPooledDataSource 26 | // val db = Database.forDataSource(cpds) 27 | // db.run(UsersTQ.getPassword("alice").result).map({ xs => 28 | // println(xs) 29 | // }) 30 | } 31 | test("user info") { 32 | // val cpds = new ComboPooledDataSource 33 | // val db = Database.forDataSource(cpds) 34 | //db.run(ExchangeApiTables.userInfo("alice").result) map { xs => 35 | // info("foo\n") 36 | // xs 37 | //} 38 | //db.run(ExchangeApiTables.users.result).onSuccess { 39 | // case Success(msg) => println(msg) 40 | // case Failure(msg) => println(msg) 41 | //} 42 | } 43 | 44 | ignore("node info") { 45 | // val cpds = new ComboPooledDataSource 46 | // val db = Database.forDataSource(cpds) 47 | // db.run(ExchangeApiTables.listUserNodes.result).map({ xs => 48 | // println(xs) 49 | // }) 50 | } 51 | 52 | ignore("get one user password") { 53 | // var cpds = new ComboPooledDataSource 54 | // var db = Database.forDataSource(cpds) 55 | //var xs = db.run(ExchangeApiTables.userPassword("alice", "password1").result) 56 | //println("starting") 57 | //xs.onComplete = println("foobar") 58 | } 59 | 60 | ignore("db-nodes") { 61 | // val cpds = new ComboPooledDataSource 62 | // val db = Database.forDataSource(cpds) 63 | // db.run(ExchangeApiTables.listUserNodes.result) map { xs => 64 | // println(xs) 65 | // assert(xs != null) 66 | //for (user <- xs){ 67 | // println(user) 68 | //} 69 | // } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | services: docker 2 | language: scala 3 | dist: focal 4 | jdk: openjdk11 5 | scala: 6 | - 2.13.5 7 | notifications: 8 | slack: 9 | secure: nPoYZ8FqRziV5+rQAc9GXFWtNsThBbOdxYOY8HpPwNWsWHtC2ZyPJBnNE6XIkmzR7+D5U8yOYqlk2CiqYLlSwa1+bqKuAyOBBhz51pDi8z+YXsjvgTpkxVmKv8N6jntAqo5eBFbVUW+/FPhKBD6qZIkbfRyThZSNZTSTv9oZ02Ynfb4NKuXgZeuinSotOaICiCvzfY4kYoe7EKss/XY6ON8qHUOcIQBsckDHrFEYwF270qNEIccZIkrOr3PKg3mXz2n+65T5i/UDNw3Z8RKDc32Y+TCfyAc3kyuQQYhKhL6/TLwAW/IPNGaFkUfR+2FH+C1VlLJpm1/mBj6uDvHBVRHSEL3ZofYFd5TTzUqkQRINATRQNpFNfjlMT3ifJrSyopKXyMsiea2y3EvM4/D3I5pHRvX2/BqidjJV3b5UIllirq/jk4PrCKshkKEZtC0CBNj4T8ewa9Qr3IxlKTFVHwnW1RWQmAXgFDFpbDzJ4vuLbUDhAKDXukWqoiwxTC3egMPQVnEFvxHVDAqdQUztsUjtg3LVXagLLl6+tYTwY53124aUXSkQMbANL+2ISZuRmg4dheTtaK/bE8L4dCQyyy7HFh03IbZGnGz3bhpDGxTBFAhnEQ4XWzYNpz8rEr0unvQTfBt2dUq4AH5bh1QseFh77lpBYWj9jxndZMbP9Pw= 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | env: 16 | - DEBIAN_FRONTEND=noninteractive 17 | DOCKER_NETWORK=exchange-api-network 18 | DOCKER_REGISTRY=openhorizon 19 | EXCHANGE_FE_HEADER=issuer 20 | EXCHANGE_ROOTPW=ci-password 21 | POSTGRES_DB=exchange 22 | POSTGRES_PORT=5432 23 | POSTGRES_USER=admin 24 | 25 | before_install: 26 | - sudo apt-get -y update 27 | - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce || true 28 | 29 | before_script: 30 | - '{ test $TRAVIS_PULL_REQUEST = ''false'' && docker login -u="$DOCKER_HUB_USER" -p="$DOCKER_HUB_PASS"; } || echo ''This is a PR, skipping the login''' 31 | - make .docker-network 32 | - docker run -d -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_DB=$POSTGRES_DB -e POSTGRES_USER=$POSTGRES_USER --network 33 | $DOCKER_NETWORK --name postgres postgres 34 | - export POSTGRES_HOST=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 35 | postgres) 36 | - sudo mkdir -p /etc/horizon/exchange 37 | - 'sudo bash -c "echo ''{ \"api\": { \"db\": { \"jdbcUrl\": \"jdbc:postgresql://$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB\", 38 | \"user\": \"$POSTGRES_USER\" }, \"root\": { \"password\": \"$EXCHANGE_ROOTPW\", 39 | \"frontEndHeader\": \"$EXCHANGE_FE_HEADER\" } } }'' > /etc/horizon/exchange/config.json"' 40 | - cat /etc/horizon/exchange/config.json 41 | - unset SBT_OPTS 42 | - make travis-test 43 | 44 | script: 45 | # Note: this does NOT run the IBM/OCP cloud tests or the multi-tenancy tests (because this travis script would have to stand up an OCP instance). Those tests must be run in your own dev environment. 46 | - make test 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/VersionSuite.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import org.scalatest.funsuite.AnyFunSuite 4 | import org.junit.runner.RunWith 5 | import org.scalatestplus.junit.JUnitRunner 6 | import com.horizon.exchangeapi._ 7 | 8 | /** 9 | * Tests for the Version and VersionRange case classes 10 | */ 11 | @RunWith(classOf[JUnitRunner]) 12 | class VersionSuite extends AnyFunSuite { 13 | test("Version tests") { 14 | assert(Version("1.2.3").isValid) 15 | assert(Version("infinity").isValid) 16 | assert(Version("Infinity").isValid) 17 | assert(Version("INFINITY").isValid) 18 | assert(!Version("1.2.3.4").isValid) 19 | assert(!Version("x").isValid) 20 | assert(Version("1.2.3").toString === "1.2.3") 21 | assert(Version("1.0.0") === Version("1")) 22 | assert(Version("1.2.3") != Version("1.3.2")) 23 | assert(Version("2.2.3") > Version("1.3.2")) 24 | assert(!(Version("1.2.3") > Version("1.3.2"))) 25 | assert(Version("infinity") > Version("1.3.2")) 26 | assert(!(Version("1.2.3") > Version("INFINITY"))) 27 | assert(Version("1.2.3") >= Version("1.2.3")) 28 | assert(Version("1.3.3") >= Version("1.2.3")) 29 | assert(!(Version("1.2.2") >= Version("1.2.3"))) 30 | } 31 | 32 | test("VersionRange tests") { 33 | assert(VersionRange("1").toString === "[1.0.0,infinity)") 34 | assert(!VersionRange("1,x").isValid) 35 | assert(VersionRange("1,infinity]").isValid) 36 | assert(VersionRange("1,INFINITY]").isValid) 37 | assert(VersionRange("1").isValid) 38 | assert(Version("1.2") in VersionRange("1")) 39 | assert(Version("1.2") notIn VersionRange(" 1, 1.1")) 40 | assert(Version("1.2") in VersionRange("1.2")) 41 | assert(Version("1.2") notIn VersionRange("(1.2")) 42 | assert(Version("1.2.3") in VersionRange("1.2,1.2.3]")) 43 | assert(Version("1.2.3") in VersionRange("1.2")) 44 | assert(Version("1.2.3") in VersionRange("1.2,")) 45 | assert(Version("1.2.3") notIn VersionRange("(1.2,1.2.3")) 46 | assert(Version("1.2.3") notIn VersionRange("(1.2,1.2.3)")) 47 | assert(Version("1.2.3") in VersionRange("1.2,infinity")) 48 | assert(Version("1.2.3") in VersionRange("1.2,INFINITY")) 49 | assert(Version("1.2.3") in VersionRange("1.2,1.4")) 50 | assert(Version("1.2.3") in VersionRange("[1.0.0,2.0.0)")) 51 | assert(Version("1.0.0") in VersionRange("[1.0.0,2.0.0)")) 52 | assert(Version("2.0.0") notIn VersionRange("[1.0.0,2.0.0)")) 53 | } 54 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/PerfSuite.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import org.json4s._ 4 | import org.junit.runner.RunWith 5 | import org.scalatest.funsuite.AnyFunSuite 6 | import org.scalatestplus.junit.JUnitRunner 7 | 8 | import scalaj.http._ 9 | import com.horizon.exchangeapi._ 10 | import org.json4s.native.Serialization.write 11 | 12 | //someday: do some short perf tests here (in addition to scr/test/go) so we get some automatic perf info 13 | 14 | /** Run some performance tests on the exchange. */ 15 | @RunWith(classOf[JUnitRunner]) 16 | class PerfSuite extends AnyFunSuite { 17 | 18 | val urlRoot = sys.env.getOrElse("EXCHANGE_URL_ROOT", "http://localhost:8080") 19 | val URL = urlRoot+"/v1" 20 | val ACCEPT = ("Accept","application/json") 21 | val CONTENT = ("Content-Type","application/json") 22 | val user = "9975" 23 | val pw = user+"pw" 24 | val AUTH = ("Authorization","Basic "+ApiUtils.encode(user+":"+pw)) 25 | val agbotId = "9930" // need to use a different id than AgbotsSuite.scala, because all of the suites run concurrently 26 | val agbotToken = agbotId+"tok" 27 | val AGBOTAUTH = ("Authorization","Basic "+ApiUtils.encode(agbotId+":"+agbotToken)) 28 | 29 | implicit val formats = DefaultFormats // Brings in default date formats etc. 30 | 31 | def createAgbot = { 32 | val input = PutAgbotsRequest(agbotToken, "agbot"+agbotId+"-norm", /*List[APattern](),*/ None, "ABC") 33 | val response = Http(URL+"/agbots/"+agbotId).postData(write(input)).method("put").headers(CONTENT).headers(ACCEPT).headers(AUTH).asString 34 | info("code: "+response.code+", response.body: "+response.body) 35 | assert(response.code === HttpCode.PUT_OK.intValue) 36 | } 37 | 38 | def doGetAgbots() = { 39 | val response: HttpResponse[String] = Http(URL+"/agbots").headers(ACCEPT).headers(AUTH).asString 40 | info("code: "+response.code) 41 | // info("code: "+response.code+", response.body: "+response.body) 42 | assert(response.code === HttpCode.OK.intValue) 43 | assert(response.body.startsWith("{")) 44 | // val getAgbotResp = parse(response.body).extract[GetAgbotsResponse] 45 | // assert(getAgbotResp.agbots.size === 1) // since the other test suites are creating some of these too, we can not know how many there are right now 46 | info("GET /agbots output verified") 47 | } 48 | 49 | ignore("Time GET /agbots") { 50 | createAgbot 51 | for (_ <- 1 to 5) { 52 | doGetAgbots() 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/TestDBConnection.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import com.horizon.exchangeapi.ExchangeApiApp.logger 4 | import com.mchange.v2.c3p0.ComboPooledDataSource 5 | import slick.util.AsyncExecutor 6 | import slick.jdbc.PostgresProfile.api.Database 7 | 8 | class TestDBConnection { 9 | ExchConfig.load() // get config file, normally in /etc/horizon/exchange/config.json 10 | ExchConfig.getHostAndPort match { 11 | case (h, pe, pu) => 12 | ExchangeApi.serviceHost = h 13 | ExchangeApi.servicePortEncrypted = pe 14 | ExchangeApi.servicePortUnencrypted = pu 15 | } 16 | 17 | val maxPoolSizeConfig: Int = ExchConfig.getInt("api.db.maxPoolSize") 18 | 19 | var cpds: ComboPooledDataSource = new ComboPooledDataSource() 20 | cpds.setAcquireIncrement(ExchConfig.getInt("api.db.acquireIncrement")) 21 | cpds.setDriverClass(ExchConfig.getString("api.db.driverClass")) //loads the jdbc driver 22 | cpds.setIdleConnectionTestPeriod(ExchConfig.getInt("api.db.idleConnectionTestPeriod")) 23 | cpds.setInitialPoolSize(ExchConfig.getInt("api.db.initialPoolSize")) 24 | cpds.setJdbcUrl(ExchConfig.getString("api.db.jdbcUrl")) 25 | cpds.setMaxConnectionAge(ExchConfig.getInt("api.db.maxConnectionAge")) 26 | cpds.setMaxIdleTimeExcessConnections(ExchConfig.getInt("api.db.maxIdleTimeExcessConnections")) 27 | cpds.setMaxPoolSize(maxPoolSizeConfig) 28 | cpds.setMaxStatementsPerConnection(ExchConfig.getInt("api.db.maxStatementsPerConnection")) 29 | cpds.setMinPoolSize(ExchConfig.getInt("api.db.minPoolSize")) 30 | cpds.setNumHelperThreads(ExchConfig.getInt("api.db.numHelperThreads")) 31 | cpds.setPassword(ExchConfig.getString("api.db.password")) 32 | cpds.setTestConnectionOnCheckin(ExchConfig.getBoolean("api.db.testConnectionOnCheckin")) 33 | cpds.setUser(ExchConfig.getString("api.db.user")) 34 | 35 | // maxConnections, maxThreads, and minThreads should all be the same size. 36 | val db: Database = 37 | if (cpds != null) { 38 | Database.forDataSource(ds = cpds, 39 | executor = AsyncExecutor(name = "ExchangeExecutor", 40 | maxConnections = maxPoolSizeConfig, 41 | maxThreads = maxPoolSizeConfig, 42 | minThreads = maxPoolSizeConfig, 43 | queueSize = ExchConfig.getInt("api.db.queueSize")), 44 | maxConnections = Option(maxPoolSizeConfig)) 45 | } 46 | else 47 | null 48 | 49 | def getDb: Database = db 50 | } 51 | -------------------------------------------------------------------------------- /src/test/bash/dropalltables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 4 | # This script will remove all of your exchange data if you give it the --yesdoit flag 5 | 6 | if [[ -z $EXCHANGE_ROOTPW ]]; then 7 | echo "Error: env var EXCHANGE_ROOTPW must be set and it must match what is in the exchange's config.json file" 8 | exit 2 9 | fi 10 | 11 | # You can override this as an env var before running this script 12 | LOCAL_EXCHANGE="http://localhost:8080" 13 | EXCHANGE_URL_ROOT="${EXCHANGE_URL_ROOT:-$LOCAL_EXCHANGE}" 14 | 15 | if [[ $1 != "--yesdoit" ]]; then 16 | echo "Specify the flag --yesdoit to delete all of the data from $EXCHANGE_URL_ROOT" 17 | exit 0 18 | fi 19 | 20 | if [[ $EXCHANGE_URL_ROOT != $LOCAL_EXCHANGE && $2 != "--iknowthisisnotlocal" ]]; then 21 | echo "You are trying to delete all of the data from remote exchange $EXCHANGE_URL_ROOT. Specify additional flag --iknowthisisnotlocal" 22 | exit 0 23 | fi 24 | 25 | appjson="application/json" 26 | accept="-H Accept:$appjson" 27 | content="-H Content-Type:$appjson" 28 | 29 | rootauth="root/root:$EXCHANGE_ROOTPW" 30 | #rootauth="root:$EXCHANGE_ROOTPW" 31 | 32 | #curlBasicArgs="-s -w %{http_code} --output /dev/null $accept" 33 | curlBasicArgs="-s -w %{http_code} $accept" 34 | # curlBasicArgs="-s -f" 35 | 36 | # Check the http code returned by curl. Args: returned rc, good rc, second good rc (optional) 37 | function checkrc { 38 | out="$1" 39 | httpcode=${out:$((${#out}-3))} # the last 3 chars are the http code 40 | if [[ $httpcode != $2 && ( -z $3 || $httpcode != $3 ) ]]; then 41 | #httpcode="${1:0:3}" # when an error occurs with curl the rest method output comes in stderr with the http code 42 | echo "===> curl failed with: $output" 43 | #exit $httpcode 44 | exit 2 45 | fi 46 | } 47 | 48 | # Check the exit code of the cmd that was run 49 | function checkexitcode { 50 | if [[ $1 != 0 ]]; then 51 | echo "===> commands failed with exit code $1" 52 | exit $1 53 | fi 54 | } 55 | 56 | function striphttpcode { 57 | s="$1" 58 | echo "${s:0:$((${#s}-3))}" 59 | } 60 | # set -x 61 | 62 | echo curl -X GET $curlBasicArgs -H "Authorization:Basic $rootauth" $EXCHANGE_URL_ROOT/v1/admin/dropdb/token 63 | output=$(curl -X GET $curlBasicArgs -H "Authorization:Basic $rootauth" $EXCHANGE_URL_ROOT/v1/admin/dropdb/token 2>&1) 64 | checkrc "$output" 200 65 | 66 | output=$(striphttpcode "$output") 67 | token=$(echo "$output" | jq -r .token) 68 | checkexitcode $? 69 | #echo "token: $token" 70 | 71 | echo curl -X POST $curlBasicArgs -H "Authorization:Basic root/root:$token" $EXCHANGE_URL_ROOT/v1/admin/dropdb 72 | output=$(curl -X POST $curlBasicArgs -H "Authorization:Basic root/root:$token" $EXCHANGE_URL_ROOT/v1/admin/dropdb 2>&1) 73 | checkrc "$output" 201 74 | echo "$output" 75 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/admin/TestAdminDropdbTokenResponse.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.admin 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.AdminDropdbTokenResponse 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestAdminDropdbTokenResponse extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = AdminDropdbTokenResponse(token = "") 13 | 14 | assert(testResponse.isInstanceOf[AdminDropdbTokenResponse]) 15 | 16 | assert(testResponse.token === "") 17 | } 18 | 19 | test("Equality") { 20 | class TestClass(variable1:String = "") 21 | 22 | val testResponse = AdminDropdbTokenResponse(token = "") 23 | val testResponse2 = AdminDropdbTokenResponse(token = "") 24 | val testResponse3 = AdminDropdbTokenResponse(token = " ") 25 | val testResponse4 = testResponse 26 | 27 | assert(!testResponse.equals(AnyRef)) 28 | assert(!testResponse.equals(TestClass)) 29 | 30 | assert(testResponse.equals(testResponse2)) 31 | assert(!testResponse.equals(testResponse3)) 32 | assert(testResponse2.equals(testResponse)) 33 | assert(!testResponse2.equals(testResponse3)) 34 | assert(testResponse.equals(testResponse4)) 35 | 36 | assert(testResponse.hashCode() === testResponse2.hashCode()) 37 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 38 | assert(testResponse.hashCode() === testResponse4.hashCode()) 39 | 40 | assert(testResponse === testResponse2) 41 | assert(testResponse !== testResponse3) 42 | assert(testResponse === testResponse4) 43 | 44 | assert(testResponse ne testResponse2) 45 | assert(testResponse ne testResponse3) 46 | assert(testResponse2 ne testResponse3) 47 | assert(testResponse eq testResponse4) 48 | } 49 | 50 | test("Copy") { 51 | val testResponse = AdminDropdbTokenResponse(token = "") 52 | val testResponse2 = testResponse.copy() 53 | val testResponse3 = testResponse.copy(token = "a") 54 | 55 | assert(testResponse2.token === "") 56 | assert(testResponse3.token === "a") 57 | 58 | assert(testResponse.equals(testResponse2)) 59 | assert(!testResponse.equals(testResponse3)) 60 | 61 | assert(testResponse.hashCode() === testResponse2.hashCode()) 62 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 63 | 64 | assert(testResponse === testResponse2) 65 | assert(testResponse !== testResponse3) 66 | 67 | assert(testResponse ne testResponse2) 68 | assert(testResponse ne testResponse3) 69 | } 70 | 71 | test("toString()") { 72 | var testResponse = AdminDropdbTokenResponse(token = "abc") 73 | 74 | assert(testResponse.toString() === "AdminDropdbTokenResponse(abc)") 75 | } 76 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/org/TestNodeHealthAgreementElement.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.org 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.NodeHealthAgreementElement 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestNodeHealthAgreementElement extends AnyFunSuite { 11 | test("Constructor") { 12 | val testElement = NodeHealthAgreementElement(lastUpdated = "") 13 | 14 | assert(testElement.isInstanceOf[NodeHealthAgreementElement]) 15 | 16 | assert(testElement.lastUpdated === "") 17 | } 18 | 19 | test("Equality") { 20 | class TestClass(variable1:String = "") 21 | 22 | val testElement = NodeHealthAgreementElement(lastUpdated = "") 23 | val testElement2 = NodeHealthAgreementElement(lastUpdated = "") 24 | val testElement3 = NodeHealthAgreementElement(lastUpdated = " ") 25 | val testElement4 = testElement 26 | 27 | assert(!testElement.equals(AnyRef)) 28 | assert(!testElement.equals(TestClass)) 29 | 30 | assert(testElement.equals(testElement2)) 31 | assert(!testElement.equals(testElement3)) 32 | assert(testElement2.equals(testElement)) 33 | assert(!testElement2.equals(testElement3)) 34 | assert(testElement.equals(testElement4)) 35 | 36 | assert(testElement.hashCode() === testElement2.hashCode()) 37 | assert(testElement.hashCode() !== testElement3.hashCode()) 38 | assert(testElement.hashCode() === testElement4.hashCode()) 39 | 40 | assert(testElement === testElement2) 41 | assert(testElement !== testElement3) 42 | assert(testElement === testElement4) 43 | 44 | assert(testElement ne testElement2) 45 | assert(testElement ne testElement3) 46 | assert(testElement2 ne testElement3) 47 | assert(testElement eq testElement4) 48 | } 49 | 50 | test("Copy") { 51 | val testElement = NodeHealthAgreementElement(lastUpdated = "") 52 | val testElement2 = testElement.copy() 53 | val testElement3 = testElement.copy(lastUpdated = "a") 54 | 55 | assert(testElement2.lastUpdated === "") 56 | assert(testElement3.lastUpdated === "a") 57 | 58 | assert(testElement.equals(testElement2)) 59 | assert(!testElement.equals(testElement3)) 60 | 61 | assert(testElement.hashCode() === testElement2.hashCode()) 62 | assert(testElement.hashCode() !== testElement3.hashCode()) 63 | 64 | assert(testElement === testElement2) 65 | assert(testElement !== testElement3) 66 | 67 | assert(testElement ne testElement2) 68 | assert(testElement ne testElement3) 69 | } 70 | 71 | test("toString()") { 72 | var testElement = NodeHealthAgreementElement(lastUpdated = "abc") 73 | 74 | assert(testElement.toString() === "NodeHealthAgreementElement(abc)") 75 | } 76 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/admin/TestAdminHashpwResponse.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.admin 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.AdminHashpwResponse 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestAdminHashpwResponse extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = AdminHashpwResponse(hashedPassword = "") 13 | 14 | assert(testResponse.isInstanceOf[AdminHashpwResponse]) 15 | 16 | assert(testResponse.hashedPassword === "") 17 | } 18 | 19 | test("Equality") { 20 | class TestClass(variable1:String = "") 21 | 22 | val testResponse = AdminHashpwResponse(hashedPassword = "") 23 | val testResponse2 = AdminHashpwResponse(hashedPassword = "") 24 | val testResponse3 = AdminHashpwResponse(hashedPassword = " ") 25 | val testResponse4 = testResponse 26 | 27 | assert(!testResponse.equals(AnyRef)) 28 | assert(!testResponse.equals(TestClass)) 29 | 30 | assert(testResponse.equals(testResponse2)) 31 | assert(!testResponse.equals(testResponse3)) 32 | assert(testResponse2.equals(testResponse)) 33 | assert(!testResponse2.equals(testResponse3)) 34 | assert(testResponse.equals(testResponse4)) 35 | 36 | assert(testResponse.hashCode() === testResponse2.hashCode()) 37 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 38 | assert(testResponse.hashCode() === testResponse4.hashCode()) 39 | 40 | assert(testResponse === testResponse2) 41 | assert(testResponse !== testResponse3) 42 | assert(testResponse === testResponse4) 43 | 44 | assert(testResponse ne testResponse2) 45 | assert(testResponse ne testResponse3) 46 | assert(testResponse2 ne testResponse3) 47 | assert(testResponse eq testResponse4) 48 | } 49 | 50 | test("Copy") { 51 | val testResponse = AdminHashpwResponse(hashedPassword = "") 52 | val testResponse2 = testResponse.copy() 53 | val testResponse3 = testResponse.copy(hashedPassword = "a") 54 | 55 | assert(testResponse2.hashedPassword === "") 56 | assert(testResponse3.hashedPassword === "a") 57 | 58 | assert(testResponse.equals(testResponse2)) 59 | assert(!testResponse.equals(testResponse3)) 60 | 61 | assert(testResponse.hashCode() === testResponse2.hashCode()) 62 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 63 | 64 | assert(testResponse === testResponse2) 65 | assert(testResponse !== testResponse3) 66 | 67 | assert(testResponse ne testResponse2) 68 | assert(testResponse ne testResponse3) 69 | } 70 | 71 | test("toString()") { 72 | var testResponse = AdminHashpwResponse(hashedPassword = "abc") 73 | 74 | assert(testResponse.toString() === "AdminHashpwResponse(abc)") 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/resources/swagger/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger UI: OAuth2 Redirect 5 | 6 | 7 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/admin/TestAdminHashpwRequest.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.admin 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.AdminHashpwRequest 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestAdminHashpwRequest extends AnyFunSuite { 11 | test("Constructor") { 12 | val testRequest = AdminHashpwRequest(password = "") 13 | 14 | assert(testRequest.isInstanceOf[AdminHashpwRequest]) 15 | 16 | assert(testRequest.password === "") 17 | 18 | assert(intercept[IllegalArgumentException]{AdminHashpwRequest(password = null)}.getMessage() === "requirement failed") 19 | } 20 | 21 | test("Equality") { 22 | class TestClass(variable1:String = "") 23 | 24 | val testRequest = AdminHashpwRequest(password = "") 25 | val testRequest2 = AdminHashpwRequest(password = "") 26 | val testRequest3 = AdminHashpwRequest(password = " ") 27 | val testRequest4 = testRequest 28 | 29 | assert(!testRequest.equals(AnyRef)) 30 | assert(!testRequest.equals(TestClass)) 31 | 32 | assert(testRequest.equals(testRequest2)) 33 | assert(!testRequest.equals(testRequest3)) 34 | assert(testRequest2.equals(testRequest)) 35 | assert(!testRequest2.equals(testRequest3)) 36 | assert(testRequest.equals(testRequest4)) 37 | 38 | assert(testRequest.hashCode() === testRequest2.hashCode()) 39 | assert(testRequest.hashCode() !== testRequest3.hashCode()) 40 | assert(testRequest.hashCode() === testRequest4.hashCode()) 41 | 42 | assert(testRequest === testRequest2) 43 | assert(testRequest !== testRequest3) 44 | assert(testRequest === testRequest4) 45 | 46 | assert(testRequest ne testRequest2) 47 | assert(testRequest ne testRequest3) 48 | assert(testRequest2 ne testRequest3) 49 | assert(testRequest eq testRequest4) 50 | } 51 | 52 | test("Copy") { 53 | val testRequest = AdminHashpwRequest(password = "") 54 | val testRequest2 = testRequest.copy() 55 | val testRequest3 = testRequest.copy(password = "a") 56 | 57 | assert(testRequest2.password === "") 58 | assert(testRequest3.password === "a") 59 | 60 | assert(testRequest.equals(testRequest2)) 61 | assert(!testRequest.equals(testRequest3)) 62 | 63 | assert(testRequest.hashCode() === testRequest2.hashCode()) 64 | assert(testRequest.hashCode() !== testRequest3.hashCode()) 65 | 66 | assert(testRequest === testRequest2) 67 | assert(testRequest !== testRequest3) 68 | 69 | assert(testRequest ne testRequest2) 70 | assert(testRequest ne testRequest3) 71 | 72 | assert(intercept[IllegalArgumentException]{testRequest.copy(password = null)}.getMessage() === "requirement failed") 73 | } 74 | 75 | test("toString()") { 76 | var testRequest = AdminHashpwRequest(password = "abc") 77 | 78 | assert(testRequest.toString() === "AdminHashpwRequest(abc)") 79 | } 80 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/service/TestGetServiceAttributeResponse.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.service 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.GetServiceAttributeResponse 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestGetServiceAttributeResponse extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = GetServiceAttributeResponse(attribute = "", value = "") 13 | 14 | assert(testResponse.isInstanceOf[GetServiceAttributeResponse]) 15 | 16 | assert(testResponse.attribute === "") 17 | assert(testResponse.value === "") 18 | } 19 | 20 | test("Equality") { 21 | class TestClass(variable1:String = "", variable2:String = "", Variable3:String = "") 22 | 23 | val testResponse = GetServiceAttributeResponse(attribute = "", value = "") 24 | val testResponse2 = GetServiceAttributeResponse(attribute = "", value = "") 25 | val testResponse3 = GetServiceAttributeResponse(attribute = " ", value = " ") 26 | val testResponse4 = testResponse 27 | 28 | assert(!testResponse.equals(AnyRef)) 29 | assert(!testResponse.equals(TestClass)) 30 | 31 | assert(testResponse.equals(testResponse2)) 32 | assert(!testResponse.equals(testResponse3)) 33 | assert(testResponse2.equals(testResponse)) 34 | assert(!testResponse2.equals(testResponse3)) 35 | assert(testResponse.equals(testResponse4)) 36 | 37 | assert(testResponse.hashCode() === testResponse2.hashCode()) 38 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 39 | assert(testResponse.hashCode() === testResponse4.hashCode()) 40 | 41 | assert(testResponse === testResponse2) 42 | assert(testResponse !== testResponse3) 43 | assert(testResponse === testResponse4) 44 | 45 | assert(testResponse ne testResponse2) 46 | assert(testResponse ne testResponse3) 47 | assert(testResponse2 ne testResponse3) 48 | assert(testResponse eq testResponse4) 49 | } 50 | 51 | test("Copy") { 52 | val testResponse = GetServiceAttributeResponse(attribute = "", value = "") 53 | val testResponse2 = testResponse.copy() 54 | val testResponse3 = testResponse.copy(attribute = "a", value = "b") 55 | 56 | assert(testResponse2.attribute === "") 57 | assert(testResponse2.value === "") 58 | assert(testResponse3.attribute === "a") 59 | assert(testResponse3.value === "b") 60 | 61 | assert(testResponse.equals(testResponse2)) 62 | assert(!testResponse.equals(testResponse3)) 63 | 64 | assert(testResponse.hashCode() === testResponse2.hashCode()) 65 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 66 | 67 | assert(testResponse === testResponse2) 68 | assert(testResponse !== testResponse3) 69 | 70 | assert(testResponse ne testResponse2) 71 | assert(testResponse ne testResponse3) 72 | } 73 | 74 | test("toString()") { 75 | var testResponse = GetServiceAttributeResponse(attribute = "abc", value = "def") 76 | 77 | assert(testResponse.toString() === "GetServiceAttributeResponse(abc,def)") 78 | } 79 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/node/TestPatternNodeResponse.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.node 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.BeforeAndAfter 5 | import org.scalatest.funsuite.AnyFunSuite 6 | import org.scalatestplus.junit.JUnitRunner 7 | 8 | import com.horizon.exchangeapi.PatternNodeResponse 9 | 10 | @RunWith(classOf[JUnitRunner]) 11 | class TestPatternNodeResponse extends AnyFunSuite { 12 | test("Constructor") { 13 | val testResponse = PatternNodeResponse(id = "", nodeType = "", publicKey = "") 14 | 15 | assert(testResponse.isInstanceOf[PatternNodeResponse]) 16 | 17 | assert(testResponse.id === "") 18 | assert(testResponse.nodeType === "") 19 | assert(testResponse.publicKey === "") 20 | } 21 | 22 | test("Equality") { 23 | class TestClass(variable1:String = "", variable2:String = "", Variable3:String = "") 24 | 25 | val testResponse = PatternNodeResponse(id = "", nodeType = "", publicKey = "") 26 | val testResponse2 = PatternNodeResponse(id = "", nodeType = "", publicKey = "") 27 | val testResponse3 = PatternNodeResponse(id = " ", nodeType = " ", publicKey = " ") 28 | val testResponse4 = testResponse 29 | 30 | assert(!testResponse.equals(AnyRef)) 31 | assert(!testResponse.equals(TestClass)) 32 | 33 | assert(testResponse.equals(testResponse2)) 34 | assert(!testResponse.equals(testResponse3)) 35 | assert(testResponse2.equals(testResponse)) 36 | assert(!testResponse2.equals(testResponse3)) 37 | assert(testResponse.equals(testResponse4)) 38 | 39 | assert(testResponse.hashCode() === testResponse2.hashCode()) 40 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 41 | assert(testResponse.hashCode() === testResponse4.hashCode()) 42 | 43 | assert(testResponse === testResponse2) 44 | assert(testResponse !== testResponse3) 45 | assert(testResponse === testResponse4) 46 | 47 | assert(testResponse ne testResponse2) 48 | assert(testResponse ne testResponse3) 49 | assert(testResponse2 ne testResponse3) 50 | assert(testResponse eq testResponse4) 51 | } 52 | 53 | test("Copy") { 54 | val testResponse = PatternNodeResponse(id = "", nodeType = "", publicKey = "") 55 | val testResponse2 = testResponse.copy() 56 | val testResponse3 = testResponse.copy(id = "a", nodeType = "b", publicKey = "c") 57 | 58 | assert(testResponse2.id === "") 59 | assert(testResponse2.nodeType === "") 60 | assert(testResponse2.publicKey === "") 61 | assert(testResponse3.id === "a") 62 | assert(testResponse3.nodeType === "b") 63 | assert(testResponse3.publicKey === "c") 64 | 65 | assert(testResponse.equals(testResponse2)) 66 | assert(!testResponse.equals(testResponse3)) 67 | 68 | assert(testResponse.hashCode() === testResponse2.hashCode()) 69 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 70 | 71 | assert(testResponse === testResponse2) 72 | assert(testResponse !== testResponse3) 73 | 74 | assert(testResponse ne testResponse2) 75 | assert(testResponse ne testResponse3) 76 | } 77 | 78 | test("toString()") { 79 | var testResponse = PatternNodeResponse(id = "abc", nodeType = "def", publicKey = "ghi") 80 | 81 | assert(testResponse.toString() === "PatternNodeResponse(abc,def,ghi)") 82 | } 83 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/policy/TestBusinessPolicyNodeResponse.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.policy 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.BusinessPolicyNodeResponse 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestBusinessPolicyNodeResponse extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = BusinessPolicyNodeResponse(id = "", nodeType = "", publicKey = "") 13 | 14 | assert(testResponse.isInstanceOf[BusinessPolicyNodeResponse]) 15 | 16 | assert(testResponse.id === "") 17 | assert(testResponse.nodeType === "") 18 | assert(testResponse.publicKey === "") 19 | } 20 | 21 | test("Equality") { 22 | class TestClass(variable1:String = "", variable2:String = "", Variable3:String = "") 23 | 24 | val testResponse = BusinessPolicyNodeResponse(id = "", nodeType = "", publicKey = "") 25 | val testResponse2 = BusinessPolicyNodeResponse(id = "", nodeType = "", publicKey = "") 26 | val testResponse3 = BusinessPolicyNodeResponse(id = " ", nodeType = " ", publicKey = " ") 27 | val testResponse4 = testResponse 28 | 29 | assert(!testResponse.equals(AnyRef)) 30 | assert(!testResponse.equals(TestClass)) 31 | 32 | assert(testResponse.equals(testResponse2)) 33 | assert(!testResponse.equals(testResponse3)) 34 | assert(testResponse2.equals(testResponse)) 35 | assert(!testResponse2.equals(testResponse3)) 36 | assert(testResponse.equals(testResponse4)) 37 | 38 | assert(testResponse.hashCode() === testResponse2.hashCode()) 39 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 40 | assert(testResponse.hashCode() === testResponse4.hashCode()) 41 | 42 | assert(testResponse === testResponse2) 43 | assert(testResponse !== testResponse3) 44 | assert(testResponse === testResponse4) 45 | 46 | assert(testResponse ne testResponse2) 47 | assert(testResponse ne testResponse3) 48 | assert(testResponse2 ne testResponse3) 49 | assert(testResponse eq testResponse4) 50 | } 51 | 52 | test("Copy") { 53 | val testResponse = BusinessPolicyNodeResponse(id = "", nodeType = "", publicKey = "") 54 | val testResponse2 = testResponse.copy() 55 | val testResponse3 = testResponse.copy(id = "a", nodeType = "b", publicKey = "c") 56 | 57 | assert(testResponse2.id === "") 58 | assert(testResponse2.nodeType === "") 59 | assert(testResponse2.publicKey === "") 60 | assert(testResponse3.id === "a") 61 | assert(testResponse3.nodeType === "b") 62 | assert(testResponse3.publicKey === "c") 63 | 64 | assert(testResponse.equals(testResponse2)) 65 | assert(!testResponse.equals(testResponse3)) 66 | 67 | assert(testResponse.hashCode() === testResponse2.hashCode()) 68 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 69 | 70 | assert(testResponse === testResponse2) 71 | assert(testResponse !== testResponse3) 72 | 73 | assert(testResponse ne testResponse2) 74 | assert(testResponse ne testResponse3) 75 | } 76 | 77 | test("toString()") { 78 | var testResponse = BusinessPolicyNodeResponse(id = "abc", nodeType = "def", publicKey = "ghi") 79 | 80 | assert(testResponse.toString() === "BusinessPolicyNodeResponse(abc,def,ghi)") 81 | } 82 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/node/TestPatternSearchHashElement.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.node 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.PatternSearchHashElement 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestPatternSearchHashElement extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = PatternSearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 13 | 14 | assert(testResponse.isInstanceOf[PatternSearchHashElement]) 15 | 16 | assert(testResponse.nodeType === "") 17 | assert(testResponse.publicKey === "") 18 | assert(testResponse.noAgreementYet === false) 19 | } 20 | 21 | test("Equality") { 22 | class TestClass(variable1:String = "", variable2:String = "", Variable3:String = "") 23 | 24 | val testResponse = PatternSearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 25 | val testResponse2 = PatternSearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 26 | val testResponse3 = PatternSearchHashElement(nodeType = " ", publicKey = " ", noAgreementYet = true) 27 | val testResponse4 = testResponse 28 | 29 | assert(!testResponse.equals(AnyRef)) 30 | assert(!testResponse.equals(TestClass)) 31 | 32 | assert(testResponse.equals(testResponse2)) 33 | assert(!testResponse.equals(testResponse3)) 34 | assert(testResponse2.equals(testResponse)) 35 | assert(!testResponse2.equals(testResponse3)) 36 | assert(testResponse.equals(testResponse4)) 37 | 38 | assert(testResponse.hashCode() === testResponse2.hashCode()) 39 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 40 | assert(testResponse.hashCode() === testResponse4.hashCode()) 41 | 42 | assert(testResponse === testResponse2) 43 | assert(testResponse !== testResponse3) 44 | assert(testResponse === testResponse4) 45 | 46 | assert(testResponse ne testResponse2) 47 | assert(testResponse ne testResponse3) 48 | assert(testResponse2 ne testResponse3) 49 | assert(testResponse eq testResponse4) 50 | } 51 | 52 | test("Copy") { 53 | val testResponse = PatternSearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 54 | val testResponse2 = testResponse.copy() 55 | val testResponse3 = testResponse.copy(nodeType = "a", publicKey = "b", noAgreementYet = true) 56 | 57 | assert(testResponse2.nodeType === "") 58 | assert(testResponse2.publicKey === "") 59 | assert(testResponse2.noAgreementYet === false) 60 | assert(testResponse3.nodeType === "a") 61 | assert(testResponse3.publicKey === "b") 62 | assert(testResponse3.noAgreementYet === true) 63 | 64 | assert(testResponse.equals(testResponse2)) 65 | assert(!testResponse.equals(testResponse3)) 66 | 67 | assert(testResponse.hashCode() === testResponse2.hashCode()) 68 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 69 | 70 | assert(testResponse === testResponse2) 71 | assert(testResponse !== testResponse3) 72 | 73 | assert(testResponse ne testResponse2) 74 | assert(testResponse ne testResponse3) 75 | } 76 | 77 | test("toString()") { 78 | var testResponse = PatternSearchHashElement(nodeType = "abc", publicKey = "def", noAgreementYet = true) 79 | 80 | assert(testResponse.toString() === "PatternSearchHashElement(abc,def,true)") 81 | } 82 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/policy/TestBusinessPolicySearchHashElement.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.policy 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.BusinessPolicySearchHashElement 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestBusinessPolicySearchHashElement extends AnyFunSuite { 11 | test("Constructor") { 12 | val testResponse = BusinessPolicySearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 13 | 14 | assert(testResponse.isInstanceOf[BusinessPolicySearchHashElement]) 15 | 16 | assert(testResponse.nodeType === "") 17 | assert(testResponse.publicKey === "") 18 | assert(testResponse.noAgreementYet === false) 19 | } 20 | 21 | test("Equality") { 22 | class TestClass(variable1:String = "", variable2:String = "", Variable3:String = "") 23 | 24 | val testResponse = BusinessPolicySearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 25 | val testResponse2 = BusinessPolicySearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 26 | val testResponse3 = BusinessPolicySearchHashElement(nodeType = " ", publicKey = " ", noAgreementYet = true) 27 | val testResponse4 = testResponse 28 | 29 | assert(!testResponse.equals(AnyRef)) 30 | assert(!testResponse.equals(TestClass)) 31 | 32 | assert(testResponse.equals(testResponse2)) 33 | assert(!testResponse.equals(testResponse3)) 34 | assert(testResponse2.equals(testResponse)) 35 | assert(!testResponse2.equals(testResponse3)) 36 | assert(testResponse.equals(testResponse4)) 37 | 38 | assert(testResponse.hashCode() === testResponse2.hashCode()) 39 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 40 | assert(testResponse.hashCode() === testResponse4.hashCode()) 41 | 42 | assert(testResponse === testResponse2) 43 | assert(testResponse !== testResponse3) 44 | assert(testResponse === testResponse4) 45 | 46 | assert(testResponse ne testResponse2) 47 | assert(testResponse ne testResponse3) 48 | assert(testResponse2 ne testResponse3) 49 | assert(testResponse eq testResponse4) 50 | } 51 | 52 | test("Copy") { 53 | val testResponse = BusinessPolicySearchHashElement(nodeType = "", publicKey = "", noAgreementYet = false) 54 | val testResponse2 = testResponse.copy() 55 | val testResponse3 = testResponse.copy(nodeType = "a", publicKey = "b", noAgreementYet = true) 56 | 57 | assert(testResponse2.nodeType === "") 58 | assert(testResponse2.publicKey === "") 59 | assert(testResponse2.noAgreementYet === false) 60 | assert(testResponse3.nodeType === "a") 61 | assert(testResponse3.publicKey === "b") 62 | assert(testResponse3.noAgreementYet === true) 63 | 64 | assert(testResponse.equals(testResponse2)) 65 | assert(!testResponse.equals(testResponse3)) 66 | 67 | assert(testResponse.hashCode() === testResponse2.hashCode()) 68 | assert(testResponse.hashCode() !== testResponse3.hashCode()) 69 | 70 | assert(testResponse === testResponse2) 71 | assert(testResponse !== testResponse3) 72 | 73 | assert(testResponse ne testResponse2) 74 | assert(testResponse ne testResponse3) 75 | } 76 | 77 | test("toString()") { 78 | var testResponse = BusinessPolicySearchHashElement(nodeType = "abc", publicKey = "def", noAgreementYet = true) 79 | 80 | assert(testResponse.toString() === "BusinessPolicySearchHashElement(abc,def,true)") 81 | } 82 | } -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/ApiUtilsSuite.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | @RunWith(classOf[JUnitRunner]) 8 | class ApiUtilsSuite extends AnyFunSuite{ 9 | 10 | test("ApiTime fixFormatting no milliseconds"){ 11 | val timeNoMilliseconds = "2019-06-17T21:24:55Z[UTC]" 12 | info(ApiTime.fixFormatting(timeNoMilliseconds) + " , " + "2019-06-17T21:24:55.000Z[UTC]") 13 | assert(ApiTime.fixFormatting(timeNoMilliseconds) == "2019-06-17T21:24:55.000Z[UTC]") 14 | } 15 | 16 | test("ApiTime fixFormatting no seconds and nomilliseconds"){ 17 | val timeNoSeconds = "2019-06-17T21:24Z[UTC]" 18 | info(ApiTime.fixFormatting(timeNoSeconds) + " , " + "2019-06-17T21:24:00.000Z[UTC]") 19 | assert(ApiTime.fixFormatting(timeNoSeconds) == "2019-06-17T21:24:00.000Z[UTC]") 20 | } 21 | 22 | test("ApiTime.nowUTC is always right length"){ 23 | info(ApiTime.nowUTC) 24 | assert(ApiTime.nowUTC.length >= 29) 25 | } 26 | 27 | test("ApiTime.thenUTC is always right time and length"){ 28 | info(ApiTime.thenUTC(1615406509)+ " , 2021-03-10T20:01:49.000Z[UTC]") 29 | assert(ApiTime.thenUTC(1615406509) == "2021-03-10T20:01:49.000Z[UTC]") 30 | assert(ApiTime.thenUTC(1615406509).length >= 29) 31 | } 32 | 33 | test("ApiTime.thenUTC is always right time and length test 2"){ 34 | info(ApiTime.thenUTC(1615406355)+ " , 2021-03-10T19:59:15.000Z[UTC]") 35 | assert(ApiTime.thenUTC(1615406355) == "2021-03-10T19:59:15.000Z[UTC]") 36 | assert(ApiTime.thenUTC(1615406355).length >= 29) 37 | } 38 | 39 | test("ApiTime.pastUTC is always right length"){ 40 | info(ApiTime.pastUTC(10)) 41 | assert(ApiTime.pastUTC(10).length >= 29) 42 | } 43 | 44 | test("ApiTime.futureUTC is always right length"){ 45 | info(ApiTime.futureUTC(10)) 46 | assert(ApiTime.futureUTC(10).length >= 29) 47 | } 48 | 49 | test("ApiTime.beginningUTC is always correct and right length"){ 50 | info(ApiTime.beginningUTC + " , " +"1970-01-01T00:00:00.000Z[UTC]") 51 | assert(ApiTime.beginningUTC == "1970-01-01T00:00:00.000Z[UTC]") 52 | assert(ApiTime.beginningUTC.length >= 29) 53 | } 54 | 55 | test("NodeAgbotTokenValidation.isValid correctly identifies if password is too short, <15 chars"){ 56 | info("NodeAgbotTokenValidation.isValid(\"1Abbb\")"+ " , " + NodeAgbotTokenValidation.isValid("1Abbb").toString()) 57 | assert(NodeAgbotTokenValidation.isValid("1Abbb") == false) 58 | } 59 | 60 | test("NodeAgbotTokenValidation.isValid correctly identifies if password does not contain digit"){ 61 | info("NodeAgbotTokenValidation.isValid(\"aaaaaaaaaaaaaaaB\")"+ " , " + NodeAgbotTokenValidation.isValid("aaaaaaaaaaaaaaaB").toString()) 62 | assert(NodeAgbotTokenValidation.isValid("aaaaaaaaaaaaaaaB") == false) 63 | } 64 | 65 | test("NodeAgbotTokenValidation.isValid correctly identifies if password does not contain uppercase letter"){ 66 | info("NodeAgbotTokenValidation.isValid(\"aaaaaaaaaaaaaaa1\")"+ " , " + NodeAgbotTokenValidation.isValid("aaaaaaaaaaaaaaa1").toString()) 67 | assert(NodeAgbotTokenValidation.isValid("aaaaaaaaaaaaaaa1") == false) 68 | } 69 | 70 | test("NodeAgbotTokenValidation.isValid correctly identifies if password does not contain lowercase letter"){ 71 | info("NodeAgbotTokenValidation.isValid(\"AAAAAAAAAAAAAB1\")"+ " , " + NodeAgbotTokenValidation.isValid("AAAAAAAAAAAAAB1").toString()) 72 | assert(NodeAgbotTokenValidation.isValid("AAAAAAAAAAAAAB1") == false) 73 | } 74 | 75 | test("NodeAgbotTokenValidation.isValid correctly identifies valid password"){ 76 | info("NodeAgbotTokenValidation.isValid(\"AAAAAAAAAAAAaB1\")"+ " , " + NodeAgbotTokenValidation.isValid("AAAAAAAAAAAAaB1").toString()) 77 | assert(NodeAgbotTokenValidation.isValid("AAAAAAAAAAAAaB1") == true) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/scala/com/horizon/exchangeapi/route/admin/TestAdminConfigRequest.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.route.admin 2 | 3 | import org.junit.runner.RunWith 4 | import org.scalatest.funsuite.AnyFunSuite 5 | import org.scalatestplus.junit.JUnitRunner 6 | 7 | import com.horizon.exchangeapi.AdminConfigRequest 8 | 9 | @RunWith(classOf[JUnitRunner]) 10 | class TestAdminConfigRequest extends AnyFunSuite { 11 | test("Constructor") { 12 | val testRequest = AdminConfigRequest(varPath = "", value = "") 13 | 14 | assert(testRequest.isInstanceOf[AdminConfigRequest]) 15 | 16 | assert(testRequest.varPath === "") 17 | 18 | assert(intercept[IllegalArgumentException]{AdminConfigRequest(varPath = null, value = "")}.getMessage() === "requirement failed") 19 | assert(intercept[IllegalArgumentException]{AdminConfigRequest(varPath = "", value = null)}.getMessage() === "requirement failed") 20 | assert(intercept[IllegalArgumentException]{AdminConfigRequest(varPath = null, value = null)}.getMessage() === "requirement failed") 21 | } 22 | 23 | test("Equality") { 24 | class TestClass(variable1:String = "") 25 | 26 | val testRequest = AdminConfigRequest(varPath = "", value = "") 27 | val testRequest2 = AdminConfigRequest(varPath = "", value = "") 28 | val testRequest3 = AdminConfigRequest(varPath = " ", value = " ") 29 | val testRequest4 = testRequest 30 | 31 | assert(!testRequest.equals(AnyRef)) 32 | assert(!testRequest.equals(TestClass)) 33 | 34 | assert(testRequest.equals(testRequest2)) 35 | assert(!testRequest.equals(testRequest3)) 36 | assert(testRequest2.equals(testRequest)) 37 | assert(!testRequest2.equals(testRequest3)) 38 | assert(testRequest.equals(testRequest4)) 39 | 40 | assert(testRequest.hashCode() === testRequest2.hashCode()) 41 | assert(testRequest.hashCode() !== testRequest3.hashCode()) 42 | assert(testRequest.hashCode() === testRequest4.hashCode()) 43 | 44 | assert(testRequest === testRequest2) 45 | assert(testRequest !== testRequest3) 46 | assert(testRequest === testRequest4) 47 | 48 | assert(testRequest ne testRequest2) 49 | assert(testRequest ne testRequest3) 50 | assert(testRequest2 ne testRequest3) 51 | assert(testRequest eq testRequest4) 52 | } 53 | 54 | test("Copy") { 55 | val testRequest = AdminConfigRequest(varPath = "", value = "") 56 | val testRequest2 = testRequest.copy() 57 | val testRequest3 = testRequest.copy(varPath = "a", value = "b") 58 | 59 | assert(testRequest2.varPath === "") 60 | assert(testRequest2.value === "") 61 | assert(testRequest3.varPath === "a") 62 | assert(testRequest3.value === "b") 63 | 64 | assert(testRequest.equals(testRequest2)) 65 | assert(!testRequest.equals(testRequest3)) 66 | 67 | assert(testRequest.hashCode() === testRequest2.hashCode()) 68 | assert(testRequest.hashCode() !== testRequest3.hashCode()) 69 | 70 | assert(testRequest === testRequest2) 71 | assert(testRequest !== testRequest3) 72 | 73 | assert(testRequest ne testRequest2) 74 | assert(testRequest ne testRequest3) 75 | 76 | assert(intercept[IllegalArgumentException]{testRequest.copy(varPath = null)}.getMessage() === "requirement failed") 77 | assert(intercept[IllegalArgumentException]{testRequest.copy(value = null)}.getMessage() === "requirement failed") 78 | assert(intercept[IllegalArgumentException]{testRequest.copy(varPath = null, value = "")}.getMessage() === "requirement failed") 79 | assert(intercept[IllegalArgumentException]{testRequest.copy(varPath = "", value = null)}.getMessage() === "requirement failed") 80 | assert(intercept[IllegalArgumentException]{testRequest.copy(varPath = null, value = null)}.getMessage() === "requirement failed") 81 | } 82 | 83 | test("toString()") { 84 | var testRequest = AdminConfigRequest(varPath = "abc", value = "def") 85 | 86 | assert(testRequest.toString() === "AdminConfigRequest(abc,def)") 87 | } 88 | } -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/SwaggerDocService.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi 2 | 3 | import akka.event.Logging.Info 4 | import akka.http.scaladsl.model.headers.LinkParams.title 5 | import akka.http.scaladsl.server.{Directives, Route} 6 | import com.github.swagger.akka.SwaggerHttpService 7 | import com.github.swagger.akka.model.{Info, License} 8 | import io.swagger.v3.oas.models.ExternalDocumentation 9 | 10 | /*Swagger references: 11 | - Swagger with akka-http: https://github.com/swagger-akka-http/swagger-akka-http 12 | - Swagger annotations: https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations 13 | */ 14 | 15 | //someday: disable the Try It Out button via the plugin suggestion: https://github.com/swagger-api/swagger-ui/issues/3725 16 | 17 | object SwaggerDocService extends SwaggerHttpService { 18 | //override implicit val actorSystem: ActorSystem = system 19 | //override implicit val materializer: ActorMaterializer = ActorMaterializer() 20 | override def apiClasses = Set( 21 | classOf[AgentConfigurationManagementRoutes], 22 | classOf[AdminRoutes], 23 | classOf[AgbotsRoutes], 24 | classOf[BusinessRoutes], 25 | classOf[CatalogRoutes], 26 | classOf[ManagementPoliciesRoutes], 27 | classOf[NodesRoutes], 28 | classOf[OrgsRoutes], 29 | classOf[PatternsRoutes], 30 | classOf[ServicesRoutes], 31 | classOf[UsersRoutes] 32 | ) 33 | override def apiDocsPath: String = "api-docs" //where you want the swagger-json endpoint exposed 34 | // override def basePath: String = "" 35 | override def host: String = (if(ExchangeApi.serviceHost.equals("0.0.0.0")) "localhost" else ExchangeApi.serviceHost) + ":" + ExchangeApi.servicePortEncrypted.getOrElse(ExchangeApi.servicePortUnencrypted) //the url of your api, not swagger's json endpoint 36 | override def info: com.github.swagger.akka.model.Info = com.github.swagger.akka.model.Info( 37 | description = "Note: Test the API with curl:

curl -sS -u <org>/iamapikey:<key> https://<host>:<port>/edge-exchange/v1/orgs/... | jq

This API specification is intended to be used by developers", 38 | version = ExchangeApi.versionText, 39 | title = "Exchange API", 40 | license = Option(License("Apache License Version 2.0", "https://www.apache.org/licenses/LICENSE-2.0"))) 41 | 42 | override def externalDocs: Option[ExternalDocumentation] = Option(new ExternalDocumentation().description("Open-horizon ExchangeAPI").url("https://github.com/open-horizon/exchange-api")) 43 | override def schemes: List[String] = if(ExchangeApi.servicePortEncrypted.isDefined) List("http", "https") else List("http") 44 | //override val securitySchemeDefinitions = Map("basicAuth" -> new BasicAuthDefinition()) 45 | override def unwantedDefinitions = Seq("Function1", "Function1RequestContextFutureRouteResult") 46 | } 47 | 48 | /* Defines a route (that can be used in a browser) to return the swagger.json file that is built by SwaggerDocService. 49 | - Swagger UI static files come from: https://github.com/swagger-api/swagger-ui/dist 50 | - The Makefile target sync-swagger-ui puts those files under src/main/resources/swagger and modifies the url value in index.html to be /v1/api-docs/swagger.json 51 | - Configuration of the UI display: https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md 52 | - Maybe explore using https://github.com/pragmatico/swagger-ui-akka-http to run the swagger ui 53 | */ 54 | trait SwaggerUiService extends Directives { 55 | val swaggerUiRoutes: Route = path("swagger") {getFromResource("swagger/index.html") } ~ getFromResourceDirectory("swagger") 56 | } 57 | 58 | /* this didn't work because swagger annotations need to be constants 59 | object SwaggerUtils { 60 | def resp(successCode: StatusCode, responseClass: Class[_], otherCodes: StatusCode*) = { 61 | val r = Array(new responses.ApiResponse(responseCode = successCode.intValue.toString, description = "Response body:", 62 | content = Array(new Content(schema = new Schema(implementation = responseClass))))) 63 | r ++ otherCodes.map(c => new responses.ApiResponse(responseCode = c.intValue.toString, description = c.reason() )).toArray 64 | } 65 | } 66 | */ 67 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/tables/AgentConfigurationManagement.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.tables 2 | 3 | import com.horizon.exchangeapi.{ApiTime, Version, VersionRange} 4 | import org.json4s._ 5 | import org.json4s.jackson.Serialization.read 6 | import slick.dbio.Effect 7 | import slick.jdbc.PostgresProfile.api._ 8 | 9 | import java.sql.Timestamp 10 | import scala.collection.mutable.ListBuffer 11 | 12 | 13 | trait AgentVersions { 14 | def agentCertVersions: Seq[String] 15 | def agentConfigVersions: Seq[String] 16 | def agentSoftwareVersions: Seq[String] 17 | } 18 | 19 | final case class AgentVersionsRequest(agentCertVersions: Seq[String], 20 | agentConfigVersions: Seq[String], 21 | agentSoftwareVersions: Seq[String]) extends AgentVersions 22 | 23 | final case class AgentVersionsResponse(agentCertVersions: Seq[String], 24 | agentConfigVersions: Seq[String], 25 | agentSoftwareVersions: Seq[String], 26 | lastUpdated: String) extends AgentVersions 27 | 28 | 29 | class AgentCertificateVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_certificate") { 30 | def certificateVersion = column[String]("version") 31 | def organization = column[String]("organization", O.Default("IBM")) 32 | def priority = column[Option[Long]]("priority") 33 | 34 | def * = (certificateVersion, organization, priority) 35 | def pkAgentVerCert = primaryKey("pk_agent_version_certificate", (organization, certificateVersion)) 36 | def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) 37 | def idxPriority = index("idx_avcert_priority", (organization, priority), unique = true) 38 | } 39 | 40 | object AgentCertificateVersionsTQ extends TableQuery(new AgentCertificateVersions(_)) { 41 | def getAgentCertificateVersions(organization: String): Query[Rep[String], String, Seq] = this.filter(_.organization === organization).map(_.certificateVersion) 42 | } 43 | 44 | 45 | class AgentConfigurationVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_configuration") { 46 | def configurationVersion = column[String]("version") 47 | def organization = column[String]("organization", O.Default("IBM")) 48 | def priority = column[Option[Long]]("priority") 49 | 50 | def * = (configurationVersion, organization, priority) 51 | def pkAgentVerConfig = primaryKey("pk_agent_version_configuration", (organization, configurationVersion)) 52 | def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) 53 | def idxPriority = index("idx_avconfig_priority", (organization, priority), unique = true) 54 | } 55 | 56 | object AgentConfigurationVersionsTQ extends TableQuery(new AgentConfigurationVersions(_)) { 57 | def getAgentConfigurationVersions(organization: String): Query[Rep[String], String, Seq] = this.filter(_.organization === organization).map(_.configurationVersion) 58 | } 59 | 60 | 61 | class AgentSoftwareVersions(tag: Tag) extends Table[(String, String, Option[Long])](tag, "agent_version_software") { 62 | def organization = column[String]("organization", O.Default("IBM")) 63 | def softwareVersion = column[String]("version") 64 | def priority = column[Option[Long]]("priority") 65 | 66 | def * = (organization, softwareVersion, priority) 67 | def pkAgentVerSoft = primaryKey("pk_agent_version_software", (organization, softwareVersion)) 68 | def fkOrg = foreignKey("fk_organization", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) 69 | def idxPriority = index("idx_avsoft_priority", (organization, priority), unique = true) 70 | } 71 | 72 | object AgentSoftwareVersionsTQ extends TableQuery(new AgentSoftwareVersions(_)) { 73 | def getAgentSoftwareVersions(organization: String): Query[Rep[String], String, Seq] = this.filter(_.organization === organization).map(_.softwareVersion) 74 | } 75 | 76 | 77 | class AgentVersionsChanged(tag: Tag) extends Table[(java.sql.Timestamp, String)](tag, "agent_version_last_updated") { 78 | def changed = column[java.sql.Timestamp]("changed") 79 | def organization = column[String]("organization", O.PrimaryKey, O.Default("IBM")) 80 | 81 | def * = (changed, organization) 82 | def fkOrg = foreignKey("fk_org", organization, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) 83 | } 84 | 85 | object AgentVersionsChangedTQ extends TableQuery(new AgentVersionsChanged(_)) { 86 | def getChanged(organization: String): Query[Rep[Timestamp], Timestamp, Seq] = this.filter(_.organization === organization).map(_.changed) 87 | } 88 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/tables/Users.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.tables 2 | 3 | import com.horizon.exchangeapi._ 4 | import slick.jdbc.PostgresProfile.api._ 5 | 6 | /** Contains the object representations of the DB tables related to users. */ 7 | 8 | //future: figure out how to use the slick type Timestamp, but have it stored in UTC 9 | final case class UserRow(username: String, orgid: String, hashedPw: String, admin: Boolean, hubAdmin: Boolean, email: String, lastUpdated: String, updatedBy: String) { 10 | def insertUser(): DBIO[_] = { 11 | //val pw = if (password == "") "" else if (Password.isHashed(password)) password else Password.hash(password) 12 | UsersTQ += UserRow(username, orgid, hashedPw, admin, hubAdmin, email, lastUpdated, updatedBy) 13 | } 14 | 15 | def upsertUser: DBIO[_] = { 16 | //val pw = if (password == "") "" else if (Password.isHashed(password)) password else Password.hash(password) 17 | UsersTQ.insertOrUpdate(UserRow(username, orgid, hashedPw, admin, hubAdmin, email, lastUpdated, updatedBy)) 18 | } 19 | 20 | def updateUser(): DBIO[_] = { 21 | //val pw = if (password == "") "" else if (Password.isHashed(password)) password else Password.hash(password) 22 | (for { u <- UsersTQ if u.username === username } yield u).update(UserRow(username, orgid, hashedPw, admin, hubAdmin, email, lastUpdated, updatedBy)) 23 | /* 24 | // if password and/or email are blank, it means they should not be updated <- not supporting this anymore 25 | (pw, email) match { 26 | case ("", "") => return (for { u <- UsersTQ if u.username === username } yield (u.username,u.orgid,u.admin,u.lastUpdated)).update((username, orgid, admin, lastUpdated)) 27 | case (_, "") => return (for { u <- UsersTQ if u.username === username } yield (u.username,u.orgid,u.password,u.admin,u.lastUpdated)).update((username, orgid, pw, admin, lastUpdated)) 28 | case ("", _) => return (for { u <- UsersTQ if u.username === username } yield (u.username,u.orgid,u.admin,u.email,u.lastUpdated)).update((username, orgid, admin, email, lastUpdated)) 29 | case (_, _) => return (for { u <- UsersTQ if u.username === username } yield u).update(UserRow(username, orgid, pw, admin, email, lastUpdated)) 30 | } 31 | */ 32 | } 33 | } 34 | 35 | /** Mapping of the users db table to a scala class */ 36 | class Users(tag: Tag) extends Table[UserRow](tag, "users") { 37 | def username = column[String]("username", O.PrimaryKey) // the content of this is orgid/username 38 | def orgid = column[String]("orgid") 39 | def password = column[String]("password") 40 | def admin = column[Boolean]("admin") 41 | def hubAdmin = column[Boolean]("hubadmin") 42 | def email = column[String]("email") 43 | // def lastUpdated = column[Timestamp]("lastupdated") //someday: need this is UTC, not local time zone 44 | def lastUpdated = column[String]("lastupdated") 45 | def updatedBy = column[String]("updatedby") 46 | def * = (username, orgid, password, admin, hubAdmin, email, lastUpdated, updatedBy).<>(UserRow.tupled, UserRow.unapply) 47 | //def primKey = primaryKey("pk_pk", (username, orgid)) 48 | def orgidKey = foreignKey("orgid_fk", orgid, OrgsTQ)(_.orgid, onUpdate=ForeignKeyAction.Cascade, onDelete=ForeignKeyAction.Cascade) 49 | } 50 | 51 | object UsersTQ extends TableQuery(new Users(_)) { 52 | //def getAllUsers(orgid: String) = this.filter(_.username like orgid+"/%") 53 | def getAllUsers(orgid: String): Query[Users, UserRow, Seq] = this.filter(_.orgid === orgid) 54 | def getAllAdmins(orgid: String): Query[Users, UserRow, Seq] = this.filter(_.orgid === orgid).filter(r => {r.admin || r.hubAdmin}) 55 | def getAllUsersUsername(orgid: String): Query[Rep[String], String, Seq] = this.filter(_.orgid === orgid).map(_.username) 56 | def getUser(username: String): Query[Users, UserRow, Seq] = this.filter(_.username === username) 57 | def getUserIfAdmin(username: String): Query[Users, UserRow, Seq] = this.filter(r => {r.username === username && (r.admin || r.hubAdmin)}) 58 | def getPassword(username: String): Query[Rep[String], String, Seq] = this.filter(_.username === username).map(_.password) 59 | def getAdmin(username: String): Query[Rep[Boolean], Boolean, Seq] = this.filter(_.username === username).map(_.admin) 60 | def getHubAdmin(username: String): Query[Rep[Boolean], Boolean, Seq] = this.filter(_.username === username).map(_.hubAdmin) 61 | //def getAdminAsString(username: String) = this.filter(_.username === username).map(u => if (u.admin === Boolean(true)) "admin" else "") 62 | def getEmail(username: String): Query[Rep[String], String, Seq] = this.filter(_.username === username).map(_.email) 63 | def getUpdatedBy(username: String): Query[Rep[String], String, Seq] = this.filter(_.username === username).map(_.updatedBy) 64 | } 65 | 66 | final case class User(password: String, admin: Boolean, hubAdmin: Boolean, email: String, lastUpdated: String, updatedBy: String) { 67 | def hidePassword: User = User(StrConstants.hiddenPw, admin, hubAdmin, email, lastUpdated, updatedBy) 68 | } 69 | -------------------------------------------------------------------------------- /src/main/resources/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "acls": { 4 | "AdminUser": [ 5 | "ALL_IN_ORG", 6 | "WRITE_AGENT_CONFIG_MGMT" 7 | ], 8 | "Agbot": [ 9 | "DATA_HEARTBEAT_MY_AGBOTS", 10 | "MAXCHANGEID", 11 | "READ_MYSELF", 12 | "READ_AGENT_CONFIG_MGMT", 13 | "READ_ALL_AGBOTS", 14 | "READ_ALL_NODES", 15 | "READ_ALL_SERVICES", 16 | "READ_ALL_PATTERNS", 17 | "READ_ALL_BUSINESS", 18 | "READ_ALL_MANAGEMENT_POLICY", 19 | "READ_MY_AGBOTS", 20 | "READ_MY_ORG", 21 | "SEND_MSG_TO_NODE", 22 | "WRITE_AGENT_CONFIG_MGMT", 23 | "WRITE_MYSELF" 24 | ], 25 | "Anonymous": [], 26 | "HubAdmin": [ 27 | "CREATE_IN_OTHER_ORGS", 28 | "CREATE_ORGS", 29 | "CREATE_USER", 30 | "DELETE_ORG", 31 | "ORGSTATUS", 32 | "READ_AGENT_CONFIG_MGMT", 33 | "READ_ALL_AGBOTS", 34 | "READ_IBM_ORGS", 35 | "READ_MY_ORG", 36 | "READ_MY_USERS", 37 | "READ_MYSELF", 38 | "READ_OTHER_ORGS", 39 | "SET_IBM_ORG_TYPE", 40 | "STATUS", 41 | "UTILITIES", 42 | "WRITE_AGENT_CONFIG_MGMT", 43 | "WRITE_ALL_AGBOTS", 44 | "WRITE_MY_ORG", 45 | "WRITE_MY_USERS", 46 | "WRITE_MYSELF", 47 | "WRITE_OTHER_ORGS" 48 | ], 49 | "Node": [ 50 | "MAXCHANGEID", 51 | "READ_AGENT_CONFIG_MGMT", 52 | "READ_ALL_AGBOTS", 53 | "READ_ALL_BUSINESS", 54 | "READ_ALL_MANAGEMENT_POLICY", 55 | "READ_ALL_PATTERNS", 56 | "READ_ALL_SERVICES", 57 | "READ_MY_ORG", 58 | "READ_MYSELF", 59 | "SEND_MSG_TO_AGBOT", 60 | "WRITE_MYSELF" 61 | ], 62 | "SuperUser": ["ALL"], 63 | "User": [ 64 | "CREATE_AGBOT", 65 | "CREATE_BUSINESS", 66 | "CREATE_NODE", 67 | "CREATE_PATTERNS", 68 | "CREATE_SERVICES", 69 | "DATA_HEARTBEAT_MY_AGBOTS", 70 | "READ_AGENT_CONFIG_MGMT", 71 | "READ_ALL_AGBOTS", 72 | "READ_ALL_BUSINESS", 73 | "READ_ALL_MANAGEMENT_POLICY", 74 | "READ_ALL_PATTERNS", 75 | "READ_ALL_SERVICES", 76 | "READ_IBM_ORGS", 77 | "READ_MY_AGBOTS", 78 | "READ_MY_BUSINESS", 79 | "READ_MY_NODES", 80 | "READ_MY_PATTERNS", 81 | "READ_MY_MANAGEMENT_POLICY", 82 | "READ_MY_ORG", 83 | "READ_MY_SERVICES", 84 | "READ_MYSELF", 85 | "STATUS", 86 | "UTILITIES", 87 | "WRITE_MY_AGBOTS", 88 | "WRITE_MY_BUSINESS", 89 | "WRITE_MY_NODES", 90 | "WRITE_MY_PATTERNS", 91 | "WRITE_MY_SERVICES", 92 | "WRITE_MYSELF" 93 | ] 94 | }, 95 | "akka": { 96 | "akka.http.server.backlog": "100", 97 | "akka.http.server.bind-timeout": "1s", 98 | "akka.http.server.idle-timeout": "60s", 99 | "akka.http.server.linger-timeout": "1m", 100 | "akka.http.server.max-connections": "1024", 101 | "akka.http.server.pipelining-limit": "1", 102 | "akka.http.server.request-timeout": "45s", 103 | "akka.http.server.server-header": "" 104 | }, 105 | "cache": { 106 | "authDbTimeoutSeconds": 15, 107 | "IAMusersMaxSize": 300, 108 | "IAMusersTtlSeconds": 300, 109 | "idsMaxSize": 47000, 110 | "idsTtlSeconds": 5400, 111 | "resourcesMaxSize": 300, 112 | "resourcesTtlSeconds": 300, 113 | "type": "guava" 114 | }, 115 | "db": { 116 | "acquireIncrement": 1, 117 | "driverClass": "org.postgresql.Driver", 118 | "idleConnectionTestPeriod": 0, 119 | "initialPoolSize": 1, 120 | "jdbcUrl": "", 121 | "maxConnectionAge": 0, 122 | "maxIdleTime": 0, 123 | "maxIdleTimeExcessConnections": 0, 124 | "maxPoolSize": 50, 125 | "maxStatementsPerConnection": 0, 126 | "minPoolSize": 1, 127 | "numHelperThreads": 3, 128 | "password": "", 129 | "queueSize": 1000, 130 | "testConnectionOnCheckin": false, 131 | "upgradeTimeoutSeconds": 180, 132 | "user": "" 133 | }, 134 | "defaults": { 135 | "businessPolicy": { 136 | "check_agreement_status": 1800, 137 | "missing_heartbeat_interval": 1800 138 | }, 139 | "msgs": { 140 | "expired_msgs_removal_interval": 1800 141 | }, 142 | "pattern": { 143 | "missing_heartbeat_interval": 1800, 144 | "check_agreement_status": 1800 145 | } 146 | }, 147 | "limits": { 148 | "maxAgbots": 1000, 149 | "maxAgreements": 0, 150 | "maxBusinessPolicies": 5000, 151 | "maxManagementPolicies": 5000, 152 | "maxMessagesInMailbox": 0, 153 | "maxNodes": 45000, 154 | "maxPatterns": 1000, 155 | "maxServices": 1000 156 | }, 157 | "logging": { 158 | "level": "INFO" 159 | }, 160 | "resourceChanges": { 161 | "cleanupInterval": 3600, 162 | "maxRecordsCap": 10000, 163 | "ttl": 14400 164 | }, 165 | "root": { 166 | "enabled": true, 167 | "password": "" 168 | }, 169 | "service": { 170 | "host": "0.0.0.0", 171 | "port": 8080, 172 | "portEncrypted": 8083, 173 | "shutdownWaitForRequestsToComplete": 60 174 | }, 175 | "tls": { 176 | "password": null, 177 | "truststore": null 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/auth/Exceptions.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.auth 2 | 3 | import akka.http.scaladsl.model.StatusCode 4 | import com.horizon.exchangeapi.{ApiRespType, ApiResponse, ExchMsg, HttpCode} 5 | import javax.security.auth.login.LoginException 6 | 7 | // Base class for all of the exchange authentication and authorization failures 8 | // See also case class AuthRejection in ApiUtils.scala that can turn any exception into a rejection 9 | //todo: make all of these final case classes 10 | class AuthException(var httpCode: StatusCode, var apiResponse: String, msg: String) extends LoginException(msg) { 11 | def toComplete = (httpCode, ApiResponse(apiResponse, getMessage)) 12 | } 13 | 14 | // These error msgs are matched by UsersSuite.scala, so change them there if you change them here 15 | final case class OrgNotFound(authInfoOrg: String) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, ExchMsg.translate("org.not.found.user.facing.error", authInfoOrg)) 16 | final case class IncorrectOrgFound(authInfoOrg: String, userInfoAcctId: String) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, ExchMsg.translate("incorrect.org.found.user.facing.error", authInfoOrg, userInfoAcctId)) 17 | final case class IncorrectOrgFoundMult(authInfoOrg: String) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, ExchMsg.translate("incorrect.org.found.user.facing.error.mult", authInfoOrg)) 18 | final case class IncorrectIcpOrgFound(requestOrg: String, clusterName: String) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, ExchMsg.translate("incorrect.org.found.user.facing.error.ICP", requestOrg, clusterName)) 19 | 20 | // Error class to use to define specific error responses from problems happening in DB threads 21 | // Note: this is not strictly an auth error, but it is handy to inherit from AuthException 22 | class DBProcessingError(httpCode: StatusCode, apiResponse: String, msg: String) extends AuthException(httpCode, apiResponse, msg) 23 | 24 | // These 2 exceptions will be caught by IbmCloudModule and Module respectively, and return false from login(). 25 | // Their http code should never be used, which is why it is an internal error if it unexpectedly is. 26 | // Only used internally: The creds werent ibm cloud creds, so return gracefully and move on to the next login module 27 | class NotIbmCredsException extends AuthException(HttpCode.INTERNAL_ERROR, ApiRespType.INTERNAL_ERROR, "not IBM cloud credentials") 28 | // The creds werent local exchange creds, so return gracefully and move on to the next login module 29 | class NotLocalCredsException extends AuthException(HttpCode.INTERNAL_ERROR, ApiRespType.INTERNAL_ERROR, "User is iamapikey or iamtoken, so credentials are not local Exchange credentials") 30 | 31 | // We are in the middle of a db migration, so cant authenticate/authorize anything else 32 | class IsDbMigrationException(msg: String = ExchMsg.translate("in.process.db.migration")) extends AuthException(HttpCode.ACCESS_DENIED, ApiRespType.ACCESS_DENIED, msg) 33 | 34 | // Exceptions for handling DB connection errors 35 | class DbTimeoutException(msg: String) extends AuthException(HttpCode.GW_TIMEOUT, ApiRespType.GW_TIMEOUT, msg) 36 | class DbConnectionException(msg: String) extends AuthException(HttpCode.BAD_GW, ApiRespType.BAD_GW, msg) 37 | 38 | class InvalidCredentialsException(msg: String = ExchMsg.translate("invalid.credentials")) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, msg) 39 | 40 | class OrgNotSpecifiedException(msg: String = ExchMsg.translate("org.not.specified")) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, msg) 41 | 42 | class AccessDeniedException(msg: String = ExchMsg.translate("access.denied")) extends AuthException(HttpCode.ACCESS_DENIED, ApiRespType.ACCESS_DENIED, msg) 43 | 44 | class BadInputException(msg: String = ExchMsg.translate("bad.input")) extends AuthException(HttpCode.BAD_INPUT, ApiRespType.BAD_INPUT, msg) 45 | 46 | class ResourceNotFoundException(msg: String = ExchMsg.translate("not.found")) extends AuthException(HttpCode.NOT_FOUND, ApiRespType.NOT_FOUND, msg) 47 | 48 | class UserCreateException(msg: String = ExchMsg.translate("error.creating.user.noargs")) extends AuthException(HttpCode.BAD_GW, ApiRespType.BAD_GW, msg) 49 | 50 | // Not currently used. The IAM token we were given was expired, or some similar problem 51 | //class BadIamCombinationException(msg: String) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, msg) 52 | 53 | // Unexpected http code or response body from an IAM API call 54 | class IamApiErrorException(msg: String) extends AuthException(HttpCode.BAD_GW, ApiRespType.BAD_GW, msg) 55 | 56 | // Didn't get a response from an IAM API after a number of retries 57 | class IamApiTimeoutException(msg: String) extends AuthException(HttpCode.GW_TIMEOUT, ApiRespType.GW_TIMEOUT, msg) 58 | 59 | // An error occurred while building the SSLSocketFactory with the self-signed cert 60 | class SelfSignedCertException(msg: String) extends AuthException(HttpCode.INTERNAL_ERROR, ApiRespType.INTERNAL_ERROR, msg) 61 | 62 | // The creds id was not found in the db 63 | class IdNotFoundException(msg: String = ExchMsg.translate("invalid.credentials")) extends AuthException(HttpCode.BADCREDS, ApiRespType.BADCREDS, msg) 64 | 65 | // The id was not found in the db when looking for owner or isPublic 66 | class IdNotFoundForAuthorizationException(msg: String = ExchMsg.translate("access.denied")) extends AuthException(HttpCode.ACCESS_DENIED, ApiRespType.ACCESS_DENIED, msg) 67 | 68 | class AuthInternalErrorException(msg: String) extends AuthException(HttpCode.INTERNAL_ERROR, ApiRespType.INTERNAL_ERROR, msg) 69 | -------------------------------------------------------------------------------- /src/test/bash/scale/scaledriver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Drives the overall process of scale testing the Exchange. See Usage below for details. 4 | 5 | EX_PERF_REPORT_DIR="${EX_PERF_REPORT_DIR:-/tmp/exchangePerf}" 6 | EX_PERF_ORG="${EX_PERF_ORG:-performancenodeagbot}" 7 | 8 | function usage { 9 | cat < [host-script-args ...] 11 | 12 | Drives the overall process of scale testing the Exchange by doing these main steps: 13 | - Uses prsync to copy the latest versions of the exchange scale scripts/binaries, host scripts, and certs to /tmp on each scale node 14 | - Removes exchange org $EX_PERF_ORG to eliminate any exchange resources leftover from a previous run (means HZN_EXCHANGE_URL, EXCHANGE_ROOTPW, and possibly EX_PERF_CERT_FILE must be set in this shell) 15 | - Uses pssh to run the specified host script on each of the hosts in the hosts file 16 | - Uses pslurp to copy the output from the hosts $EX_PERF_REPORT_DIR dir to the same dir on this machine (separated into hostname subdirs) 17 | - Inspects all of the error files to report how many errors occurred 18 | 19 | This script is dependent on the parallel-ssh suite of commands, see https://www.cyberciti.biz/cloud-computing/how-to-use-pssh-parallel-ssh-program-on-linux-unix/ 20 | for installing and using them. 21 | 22 | The hosts-file is a simple list of hosts the scale test should be run on. The hosts are in the standard ssh format: @ 23 | 24 | The host-script is a script you create in the current dir that is the top-level script that is run on each scale node. 25 | This script can, for example, run different tests on different scale nodes by checking the scale node hostname. 26 | It is a assumed that the host-script, and any other scripts it calls, are in the current dir. Those will all be 27 | synced to the scale nodes. 28 | 29 | Best practices for your host scripts: 30 | - Sensitive info (e.g. exchange creds) should be received via cmd line args. The additional args passed into this scale driver script will be passed along to each host script. 31 | - At the beginning remove possible output files on that scale node leftover from previous runs, so you do not have any confusing data leftover from previous runs 32 | - Verify required software is already installed on that scale node 33 | - Save a summary of the testing on that scale node in a file that ends with .summary 34 | EOF 35 | 36 | exit 1 37 | } 38 | 39 | # Check the exit code of the cmd that was run 40 | function checkexitcode { 41 | if [[ $1 == 0 ]]; then return; fi 42 | # write error msg to both the summary file and stderr 43 | echo "===============> command $2 failed with exit code $1, exiting." 44 | exit 3 45 | #if [[ "$3" != 'continue' ]]; then exit $1; fi 46 | } 47 | 48 | function confirmcmds { 49 | for c in $*; do 50 | if ! which $c >/dev/null; then 51 | echo "Error: $c is not installed but required, exiting" 52 | exit 2 53 | fi 54 | done 55 | } 56 | 57 | function linecount { 58 | wc -l "$1" | awk '{ print $1 }' 59 | } 60 | 61 | 62 | if [[ -z $2 ]]; then usage; fi 63 | 64 | hostsFile="$1" 65 | hostScript="$2" 66 | if [[ ! -x "$hostScript" ]]; then 67 | echo "Error: $hostScript must be in the current directory and executable" 68 | exit 2 69 | fi 70 | 71 | confirmcmds pssh prsync pslurp 72 | 73 | echo "Copying the exchange scripts to /tmp on each scale node..." 74 | exchScriptDir=$(dirname $0) 75 | #pscp -h $hostsFile $exchScriptDir/* /tmp 76 | prsync -h $hostsFile -r $exchScriptDir/ /tmp/ 77 | checkexitcode $? "copying $exchScriptDir" 78 | 79 | goos="$GOOS" # set this explicitly to, for example, drive this process from mac but run the scale instances on linux 80 | if [[ -z "$goos" ]]; then # then assume the scale instances are the same type as this machine 81 | if [[ $(uname) == "Darwin" ]]; then 82 | goos="darwin" 83 | else 84 | goos="linux" 85 | fi 86 | fi 87 | exchBinDir="$exchScriptDir/../../go/$goos" 88 | echo "Building the exchange binaries for $goos and copying them from $exchBinDir to /tmp on each scale node..." 89 | make -C "$exchScriptDir/../../go" $goos/node $goos/agbot 90 | #pscp -h $hostsFile $exchBinDir/* /tmp 91 | prsync -h $hostsFile -r $exchBinDir/ /tmp/ 92 | checkexitcode $? "copying $exchBinDir" 93 | 94 | echo "Copying the host scripts and certs to /tmp on each scale node..." 95 | prsync -h $hostsFile -r ./ /tmp 96 | checkexitcode $? "copying host scripts" 97 | 98 | echo "Removing orgs/$EX_PERF_ORG from exchange '$HZN_EXCHANGE_URL'..." 99 | $exchScriptDir/deleteperforg.sh 100 | checkexitcode $? "remove orgs/ $EX_PERF_ORG" 101 | 102 | printf "\nRunning $hostScript on $(linecount $hostsFile) scale nodes...\n" 103 | pssh -h "$hostsFile" -t 0 -P "/tmp/$hostScript" ${@:3} 104 | checkexitcode $? "Running $hostScript on scale nodes" 105 | 106 | if [[ $(cat $hostsFile) == "localhost" ]]; then 107 | # This is just a special case so i can run some small tests right on this machine 108 | printf "\nScale run completed, moving the output files from $EX_PERF_REPORT_DIR to $EX_PERF_REPORT_DIR/localhost on this host...\n" 109 | mkdir -p "$EX_PERF_REPORT_DIR/localhost" 110 | rm -rf "$EX_PERF_REPORT_DIR/localhost/*" 111 | mv $(/bin/ls -d $EX_PERF_REPORT_DIR/* | grep -v localhost) $EX_PERF_REPORT_DIR/localhost 112 | else 113 | printf "\nScale run completed, gathering the output files from $EX_PERF_REPORT_DIR on the scale nodes to $EX_PERF_REPORT_DIR on this host...\n" 114 | rm -rf $EX_PERF_REPORT_DIR/* # remove output files from previous runs 115 | pslurp -h "$hostsFile" -r -L "$EX_PERF_REPORT_DIR" "$EX_PERF_REPORT_DIR/*" . 116 | checkexitcode $? "Gathering output from scale nodes" 117 | fi 118 | 119 | printf "\nInspecting the output files in $EX_PERF_REPORT_DIR for errors...\n" 120 | allSummaryFiles=$(/bin/ls $EX_PERF_REPORT_DIR/*/*/*.summary 2>/dev/null) 121 | if [[ -z "$allSummaryFiles" ]]; then 122 | echo "No errors occurred during the scale run, but no output summaries were produced either." 123 | exit 2 124 | fi 125 | # the -l flag of grep returns only the file names of files that contain the pattern (not the matched text) 126 | errorFiles=$(grep -l 'Error:==' $EX_PERF_REPORT_DIR/*/*/*.summary 2>/dev/null) 127 | if [[ -z "$errorFiles" ]]; then 128 | echo "Scale run was 100% successful!" 129 | echo "Scale node summaries can be viewed with: head -n 100 $EX_PERF_REPORT_DIR/*/*/*.summary" 130 | else 131 | printf "Errors occurred in the scale run, see files:\n$errorFiles\n" 132 | echo "View the errors and summaries with: head -n 100 $EX_PERF_REPORT_DIR/*/*/*.summary" 133 | fi 134 | -------------------------------------------------------------------------------- /src/main/scala/com/horizon/exchangeapi/auth/Module.scala: -------------------------------------------------------------------------------- 1 | package com.horizon.exchangeapi.auth 2 | 3 | import com.horizon.exchangeapi._ 4 | import javax.security.auth._ 5 | import javax.security.auth.callback._ 6 | import javax.security.auth.login.FailedLoginException 7 | import javax.security.auth.spi.LoginModule 8 | 9 | import scala.util._ 10 | 11 | /** 12 | * JAAS module to authenticate local user/pw, nodeid/token, and agbotid/token in the exchange. 13 | * Called from AuthenticationSupport:authenticate() because JAAS.config references this module. 14 | */ 15 | class Module extends LoginModule with AuthorizationSupport { 16 | private var subject: Subject = _ 17 | private var handler: CallbackHandler = _ 18 | private var identity: Identity = _ 19 | private var succeeded = false 20 | def logger = ExchangeApi.defaultLogger 21 | 22 | override def initialize( 23 | subject: Subject, 24 | handler: CallbackHandler, 25 | sharedState: java.util.Map[String, _], 26 | options: java.util.Map[String, _]): Unit = { 27 | this.subject = subject 28 | this.handler = handler 29 | } 30 | 31 | /* 32 | * This is where the actual login logic is performed, and is called by the 33 | * LoginContext when its login method is called. This uses the callback to 34 | * get acces to the web request, and then uses the logic from the credsAndLog 35 | * to get an Identity from the request. This is later attached to the subject 36 | * in the commit method, which is called by the context after login succeeds, 37 | * and that is where we can get access to it in the route handling code. 38 | */ 39 | override def login(): Boolean = { 40 | //logger.debug("in Module.login() to try to authenticate a local exchange user") 41 | val reqCallback = new RequestCallback 42 | val loginResult = Try { 43 | handler.handle(Array(reqCallback)) 44 | if (reqCallback.request.isEmpty) { 45 | logger.debug("Unable to get HTTP request while authenticating") 46 | throw new AuthInternalErrorException(ExchMsg.translate("unable.to.get.http.request.when.authenticating")) 47 | } 48 | val reqInfo = reqCallback.request.get // reqInfo is of type RequestInfo 49 | //logger.debug(s"auth/Module.login(): reqInfo: $reqInfo") 50 | //val clientIp = req.header("X-Forwarded-For").orElse(Option(req.getRemoteAddr)).get // haproxy inserts the real client ip into the header for us 51 | 52 | // Get the creds from the request 53 | val (org, id) = IbmCloudAuth.compositeIdSplit(reqInfo.creds.id) 54 | if (org == "") throw new OrgNotSpecifiedException 55 | if (id == "iamapikey" || id == "iamtoken") throw new NotLocalCredsException 56 | //logger.info("User or id " + userOrId + " from " + clientIp + " running " + req.getMethod + " " + req.getPathInfo) 57 | if (reqInfo.isDbMigration && !Role.isSuperUser(reqInfo.creds.id)) throw new IsDbMigrationException() 58 | identity = IIdentity(reqInfo.creds).authenticate(reqInfo.hint) // authenticate() is in AuthorizationSupport and both authenticates this identity and returns the correct IIdentity subclass (IUser, Inode, or IAgbot) 59 | //} 60 | true 61 | } 62 | //logger.debug("Module.login(): loginResult=" + loginResult) 63 | succeeded = loginResult.isSuccess 64 | if (!succeeded) { 65 | // Throw an exception so we can report the correct error 66 | loginResult.failed.get match { 67 | case _: NotLocalCredsException => return false 68 | case e: AuthException => throw e 69 | case _ => throw new FailedLoginException 70 | } 71 | } 72 | succeeded 73 | } 74 | 75 | override def logout(): Boolean = { 76 | subject.getPrivateCredentials().add(identity) 77 | true 78 | } 79 | 80 | override def abort() = false 81 | 82 | override def commit(): Boolean = { 83 | if (succeeded) { 84 | subject.getPrivateCredentials().add(identity) 85 | //subject.getPrincipals().add(ExchangeRole(identity.role)) // don't think we need this 86 | } 87 | succeeded 88 | } 89 | } 90 | 91 | /* 92 | * Login modules get info about the subject (like username and password) 93 | * through callbacks. JAAS doesn't really seem to have been meant for web 94 | * apps (a lot of the callbacks seem to be meant to interact with the user, 95 | * e.g., prompt them to type in a password), and after some research I 96 | * found some people were implementing it in web apps by creating callbacks 97 | * that return the http request object, so I went with that. It's possible 98 | * that we will want to support more callbacks like Name and Password, which 99 | * we can pull from the request in the callback handler, but grabbing the 100 | * request like this made it easy to re-use the existing logic. 101 | */ 102 | class ExchCallbackHandler(request: RequestInfo) extends CallbackHandler { 103 | override def handle(callbacks: Array[Callback]): Unit = { 104 | for (callback <- callbacks) { 105 | callback match { 106 | case cb: RequestCallback => cb.request = request 107 | case _ => 108 | } 109 | } 110 | } 111 | } 112 | 113 | class RequestCallback extends Callback { 114 | private var req: Option[RequestInfo] = None 115 | 116 | def request_=(request: RequestInfo): Unit = { 117 | req = Some(request) 118 | } 119 | 120 | def request: Option[RequestInfo] = req 121 | } 122 | 123 | /* Everything below here is for JAAS authorization, but not using the java authorization framework anymore, because it doesn't add any value for us and adds complexity 124 | // Both ExchangeRole and AccessPermission are listed in resources/auth.policy 125 | final case class ExchangeRole(role: String) extends Principal { 126 | override def getName = role 127 | } 128 | 129 | final case class AccessPermission(name: String) extends BasicPermission(name) 130 | 131 | final case class PermissionCheck(permission: String) extends PrivilegedAction[Unit] { 132 | import Access._ 133 | 134 | // It is easier to list the actions an admin user is *not* allowed to do 135 | private val adminNotAllowed = Set( 136 | CREATE_ORGS.toString, 137 | READ_OTHER_ORGS.toString, 138 | WRITE_OTHER_ORGS.toString, 139 | CREATE_IN_OTHER_ORGS.toString, 140 | SET_IBM_ORG_TYPE.toString, 141 | ADMIN.toString) 142 | 143 | private def isAdminAllowed(permission: String) = { 144 | if (adminNotAllowed.contains(permission)) { 145 | Failure(new Exception(ExchMsg.translate("admins.not.given.permission", permission))) 146 | } else { 147 | Success(()) 148 | } 149 | } 150 | 151 | override def run() = { 152 | val literalCheck = Try(AccessController.checkPermission(AccessPermission(permission))) 153 | lazy val adminCheck = for { 154 | _ <- isAdminAllowed(permission) //note: changed allowed to _ to make the editor happy 155 | ok <- Try(AccessController.checkPermission(AccessPermission("ALL_IN_ORG"))) 156 | } yield ok 157 | lazy val superCheck = Try(AccessController.checkPermission(AccessPermission("ALL"))) 158 | 159 | for { 160 | literalFailure <- literalCheck.failed 161 | _ <- adminCheck.failed 162 | _ <- superCheck.failed 163 | } { 164 | throw literalFailure 165 | } 166 | } 167 | } 168 | */ 169 | 170 | --------------------------------------------------------------------------------