diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..f730dff --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,80 @@ +version: 2 +defaults: &defaults + docker: + - image: circleci/python:2.7.18-stretch-browsers +install_dependency: &install_dependency + name: Installation of build and deployment dependencies. + command: | + sudo apt install jq + sudo pip install awscli --upgrade + sudo pip install docker-compose +install_deploysuite: &install_deploysuite + name: Installation of install_deploysuite. + command: | + git clone --branch v1.4.2 https://github.com/topcoder-platform/tc-deploy-scripts ../buildscript + cp ./../buildscript/master_deploy.sh . + cp ./../buildscript/buildenv.sh . + cp ./../buildscript/awsconfiguration.sh . +restore_cache_settings_for_build: &restore_cache_settings_for_build + key: docker-node-modules-{{ checksum "package-lock.json" }} + +save_cache_settings: &save_cache_settings + key: docker-node-modules-{{ checksum "package-lock.json" }} + paths: + - node_modules + +builddeploy_steps: &builddeploy_steps + - checkout + - setup_remote_docker + - run: *install_dependency + - run: *install_deploysuite + - restore_cache: *restore_cache_settings_for_build + - run: ./build.sh ${APPNAME} + - save_cache: *save_cache_settings + - deploy: + name: Running MasterScript. + command: | + ./awsconfiguration.sh $DEPLOY_ENV + source awsenvconf + ./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-deployvar + source buildenvvar + ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME} + + +jobs: + # Build & Deploy against development backend + "build-dev": + <<: *defaults + environment: + DEPLOY_ENV: "DEV" + LOGICAL_ENV: "dev" + APPNAME: "ubahn_api" + steps: *builddeploy_steps + + "build-prod": + <<: *defaults + environment: + DEPLOY_ENV: "PROD" + LOGICAL_ENV: "prod" + APPNAME: "ubahn_api" + steps: *builddeploy_steps + +workflows: + version: 2 + build: + jobs: + # Development builds are executed on "develop" branch only. + - "build-dev": + context : org-global + filters: + branches: + only: + - develop + + # Production builds are exectuted only on tagged commits to the + # master branch. + - "build-prod": + context : org-global + filters: + branches: + only: master diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fe341ce --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.idea +**/.DS_Store +.env +coverage diff --git a/.gitignore b/.gitignore index 6704566..1fd8050 100644 --- a/.gitignore +++ b/.gitignore @@ -1,104 +1,8 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage +node_modules +.idea +**/.DS_Store +upload +scripts/generate .nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file .env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port +coverage diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3e0cbaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:12 +WORKDIR /usr/src/app +COPY package.json ./ +COPY app.js ./ +COPY ./src ./src +COPY ./config ./config +COPY ./scripts ./scripts + +RUN npm install +ENTRYPOINT [ "node", "app.js" ] diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 66e4b12..4dde100 --- a/README.md +++ b/README.md @@ -1,2 +1,200 @@ -# u-bahn-api -Universal Identity API +# U-Bahn API + +## Install software + +- node 12.x +- npm 6.x +- docker +- elasticsearch 6.x + +## Configuration + +Configuration for the application is at config/default.js and config/production.js. The following parameters can be set in config files or in env variables: + +- LOG_LEVEL: the log level +- PORT: the server port +- AUTH_SECRET: TC Authentication secret +- VALID_ISSUERS: valid issuers for TC authentication +- PAGE_SIZE: the default pagination limit +- API_VERSION: the API version +- AWS_ACCESS_KEY_ID: The AWS access key +- AWS_SECRET_ACCESS_KEY: The AWS secret key +- AWS_REGION: The Amazon region to use when connecting. +- DATABASE: The QLDB ledger name +- AUTH0_URL: Auth0 URL, used to get TC M2M token +- AUTH0_AUDIENCE: Auth0 audience, used to get TC M2M token +- TOKEN_CACHE_TIME: Auth0 token cache time, used to get TC M2M token +- AUTH0_CLIENT_ID: Auth0 client id, used to get TC M2M token +- AUTH0_CLIENT_SECRET: Auth0 client secret, used to get TC M2M token +- AUTH0_PROXY_SERVER_URL: Proxy Auth0 URL, used to get TC M2M token +- GROUP_API_URL: Topcoder Group API URL +- BUSAPI_URL: Topcoder Bus API URL +- KAFKA_ERROR_TOPIC: The error topic at which bus api will publish any errors +- KAFKA_MESSAGE_ORIGINATOR: The originator value for the kafka messages +- UBAHN_CREATE_TOPIC: Kafka topic for create message +- UBAHN_UPDATE_TOPIC: Kafka topic for update message +- UBAHN_DELETE_TOPIC: Kafka topic for delete message +- ES.HOST: Elasticsearch host +- ES.API_VERSION: Elasticsearch API version +- ES.DOCUMENTS: Elasticsearch index, type and id mapping for resources. + +For `ES.DOCUMENTS` configuration, you will find multiple other configurations below it. Each has default values that you can override using the environment variables + +## Local deployment + +Setup your Elasticsearch instance and ensure that it is up and running. + +1. Visit [this link](https://console.aws.amazon.com/qldb/home?region=us-east-1#gettingStarted), login and create one **ledger** databases named `ubahn-db` +2. Visit [this link](https://console.aws.amazon.com/iam/home?region=us-east-1#/security_credentials) to download your "Access keys" +3. Follow *Configuration* section to update config values, like database, aws key/secret etc .. +4. Goto *UBahn-api*, run `npm i` and `npm run lint` +5. Import mock data, `node scripts/db/genData.js`, this will create tables and gen some data for test (if you need this) +6. Startup server `node app.js` or `npm run start` + +## Working with mock data + +You can use the scripts `npm run insert-data` (and `npm run delete-data`) to insert mock data (and delete mock data respectively). The data is inserted into QLDB and Elasticsearch. You need to setup the configurations beforehand and also start the elasticsearch instance before you run these scripts + +## Local Deployment with Docker + +Make sure all config values are right(aws key and secret), and you can run on local successful, then run below commands + +1. Navigate to the directory `docker` + +2. Rename the file `sample.api.env` to `api.env` + +3. Set the required AUTH0 configurations, AWS credentials and ElasticSearch host in the file `api.env` + +4. Once that is done, run the following command + + ```bash + docker-compose up + ``` + +5. When you are running the application for the first time, It will take some time initially to download the image and install the dependencies + +## API endpoints verification + +1. open postman +2. import *docs/UBahn_API.postman_collection.json* , *UBahn_ENV.postman_environment.json* and then check endpoints + +## Test token + +you can use below token to test role and permissions + +### 01 Topcoder User + +- payload + + ```json + { + "roles": [ + "Topcoder User" + ], + "iss": "https://api.topcoder.com", + "handle": "tc-user", + "exp": 1685571460, + "userId": "23166766", + "iat": 1585570860, + "email": "tc-user@gmail.com", + "jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9627" + } + ``` + +- token + + ```text + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLXVzZXIiLCJleHAiOjE2ODU1NzE0NjAsInVzZXJJZCI6IjIzMTY2NzY2IiwiaWF0IjoxNTg1NTcwODYwLCJlbWFpbCI6InRjLXVzZXJAZ21haWwuY29tIiwianRpIjoiMGYxZWYxZDMtMmIzMy00OTAwLWJiNDMtNDhmMjI4NWY5NjI3In0.eBhXqSBe8zMRg2nBeGeZDgKiJdAYs0zOMzGfJCjWfcs + ``` + +#### 02 Copilot + +- payload + + ```json + { + "roles": [ + "Topcoder User","Copilot" + ], + "iss": "https://api.topcoder.com", + "handle": "tc-Copilot", + "exp": 1685571460, + "userId": "23166767", + "iat": 1585570860, + "email": "tc-Copilot@gmail.com", + "jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9628" + } + ``` + +- token + + ```json + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29waWxvdCJdLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci5jb20iLCJoYW5kbGUiOiJ0Yy1Db3BpbG90IiwiZXhwIjoxNjg1NTcxNDYwLCJ1c2VySWQiOiIyMzE2Njc2NyIsImlhdCI6MTU4NTU3MDg2MCwiZW1haWwiOiJ0Yy1Db3BpbG90QGdtYWlsLmNvbSIsImp0aSI6IjBmMWVmMWQzLTJiMzMtNDkwMC1iYjQzLTQ4ZjIyODVmOTYyOCJ9.gP5JqJGCnOjO_gYs2r3-AQt5x8YIym15m3t43603cgc + ``` + +#### 03 Admin + +- payload + + ```json + { + "roles": [ + "Topcoder User","Copilot","Admin" + ], + "iss": "https://api.topcoder.com", + "handle": "tc-Admin", + "exp": 1685571460, + "userId": "23166768", + "iat": 1585570860, + "email": "tc-Admin@gmail.com", + "jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9630" + } + ``` + +- token + + ```json + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29waWxvdCIsIkFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLUFkbWluIiwiZXhwIjoxNjg1NTcxNDYwLCJ1c2VySWQiOiIyMzE2Njc2OCIsImlhdCI6MTU4NTU3MDg2MCwiZW1haWwiOiJ0Yy1BZG1pbkBnbWFpbC5jb20iLCJqdGkiOiIwZjFlZjFkMy0yYjMzLTQ5MDAtYmI0My00OGYyMjg1Zjk2MzAifQ.eR97kePT0Gu-t7vUE0Ed8A88Dnmtgebyml2jrRyxhOk + ``` + +#### M2M token 01 + +- payload, this token missing `all:usersSkill`, so all endpoints in usersSkill group will return 403 + + ```json + { + "scopes": "all:user all:role all:skill all:usersRole all:organization all:skillsProvider", + "iss": "https://api.topcoder.com", + "handle":"tc-mm-01", + "exp": 1685571460, + "iat": 1585570860, + "jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9630" + } + ``` + +- token + + ```json + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZXMiOiJhbGw6dXNlciBhbGw6cm9sZSBhbGw6c2tpbGwgYWxsOnVzZXJzUm9sZSBhbGw6b3JnYW5pemF0aW9uIGFsbDpza2lsbHNQcm92aWRlciIsImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLW1tLTAxIiwiZXhwIjoxNjg1NTcxNDYwLCJpYXQiOjE1ODU1NzA4NjAsImp0aSI6IjBmMWVmMWQzLTJiMzMtNDkwMC1iYjQzLTQ4ZjIyODVmOTYzMCJ9.BlDIYsCTcHTib9XhpyzpO-KkMTTMy0egq_7qlLWRmoM + ``` + +#### M2M token 02 + +- payload, this token contains scope, can request all endpoints + + ```json + { + "scopes": "all:user all:role all:skill all:usersRole all:organization all:skillsProvider all:usersSkill all:externalProfile all:achievementsProvider all:achievement all:attributeGroup all:attribute all:userAttribute", + "iss": "https://api.topcoder.com", + "handle": "tc-mm-02", + "exp": 1685571460, + "iat": 1585570860, + "jti": "0f1ef1d3-2b33-4900-bb43-48f2285f9630" + } + ``` + +- token + + ```json + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZXMiOiJhbGw6dXNlciBhbGw6cm9sZSBhbGw6c2tpbGwgYWxsOnVzZXJzUm9sZSBhbGw6b3JnYW5pemF0aW9uIGFsbDpza2lsbHNQcm92aWRlciBhbGw6dXNlcnNTa2lsbCBhbGw6ZXh0ZXJuYWxQcm9maWxlIGFsbDphY2hpZXZlbWVudHNQcm92aWRlciBhbGw6YWNoaWV2ZW1lbnQgYWxsOmF0dHJpYnV0ZUdyb3VwIGFsbDphdHRyaWJ1dGUgYWxsOnVzZXJBdHRyaWJ1dGUiLCJpc3MiOiJodHRwczovL2FwaS50b3Bjb2Rlci5jb20iLCJoYW5kbGUiOiJ0Yy1tbS0wMiIsImV4cCI6MTY4NTU3MTQ2MCwiaWF0IjoxNTg1NTcwODYwLCJqdGkiOiIwZjFlZjFkMy0yYjMzLTQ5MDAtYmI0My00OGYyMjg1Zjk2MzAifQ.8XJahLdv9mkgkL7EsOwsf8uKg4J9u-1UM73pvZ9n3JY + ``` diff --git a/VERIFICATION.md b/VERIFICATION.md new file mode 100644 index 0000000..5d6c7f9 --- /dev/null +++ b/VERIFICATION.md @@ -0,0 +1,102 @@ +## Verification +The verification needs data in ES, there are two ways to populate data to ES. + +Please install and run [kibana](https://www.elastic.co/downloads/past-releases/kibana-6-8-0), validate data in ES during the testing, I found this is very useful for developing and testing. +The data in ES is confusing, please validate data in ES before making a decision success or failure. + +#### Using u-bahn-api service +Start the `u-bahn-api` service and create data with postman. +As you create data, verify data is read from ES using resource GET methods from postman. +When create a resource, the postman sets the Id of corresponding resource, so be carefully after create resource + +According to the [forum](https://apps.topcoder.com/forums/?module=Thread&threadID=956692&start=0), +the u-bahn topics isn't set up in TC bus api, so the posting message to bus API doesn't work currently. +Fix TC bus api to accept u-bahn topics, please use this approach, this is the easiest way. + + +#### Using u-bahn-es-processor + +Publish messages to [u-bahn-es-processor](https://github.com/topcoder-platform/u-bahn-es-processor) through kafka. +This is very hard and error prune, but if this is the only way, use this approach. + +Follow the instructions in the README file in the `u-bahm-es-processor`, start kafka server, start elasticsearch, initialize Elasticsearch, start `u-bahn-es-processor` app + +1. start kafka-console-producer to write messages to `u-bahn.action.create` +topic: + `docker exec -it ubahn-data-processor-es_kafka /opt/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic u-bahn.action.create` +3. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"user","id":"391a3656-9a01-47d4-8c6d-64b68c44f212","handle":"user"}}` +4. Watch the app console, It will show message successfully handled. +5. Get the user from the postman by GET /users or /users/391a3656-9a01-47d4-8c6d-64b68c44f212. + +6. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"achievement","userId":"391a3656-9a01-47d4-8c6d-64b68c44f212","achievementsProviderId":"c77326d8-ef16-4be0-b844-d5c384b7bb8b","name":"achievement","uri":"https://google.com","certifierId":"b8726ca1-557e-4502-8f9b-25044b9c123d","certifiedDate":"2019-07-08T00:00:00.000Z"}}` +7. Watch the app console, It will show message successfully handled. +8. Get the achievement from the postman. + +9. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"achievementprovider","id":"c77326d8-ef16-4be0-b844-d5c384b7bb8b","name":"achievementprovider"}}` +10. Watch the app console, It will show message successfully handled. +11. Get the achievementprovider from the postman. + +12. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"attributegroup","id":"720c34f9-0fd4-46fd-9293-4a8cfdcd3e96","organizationId":"017733ad-4704-4c7e-ae60-36b3332731df","name":"attributegroup"}}` +13. Watch the app console, It will show message successfully handled. +14. Get the attributegroup from the postman. + +15. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"externalprofile","userId":"391a3656-9a01-47d4-8c6d-64b68c44f212","organizationId":"017733ad-4704-4c7e-ae60-36b3332731df","uri":"https:google.com"}}` +16. Watch the app console, It will show message successfully handled. +17. Get the externalprofile from the postman. + +18. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"organization","id":"017733ad-4704-4c7e-ae60-36b3332731df","name":"organization"}}` +19. Watch the app console, It will show message successfully handled. +20. Get the organization from the postman. + +21. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"role","id":"188446f1-02dc-4fc7-b74e-ab7ea3033a57","name":"role"}}` +22. Watch the app console, It will show message successfully handled. +23. Get the role from the postman. + +24. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"skill","id":"8a8c8d3a-9165-4dae-8a8c-f828cbe01d5d","skillProviderId":"63061b84-9784-4b71-b695-4a777eeb7601","externalId":"ba395d36-6ce8-4bd1-9d6c-754f0389abcb","uri":"https://google.com","name":"skill"}}` +25. Watch the app console, It will show message successfully handled. +26. Get the skill from the postman. + +27. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"skillprovider","id":"63061b84-9784-4b71-b695-4a777eeb7601","name":"skillprovider"}}` +28. Watch the app console, It will show message successfully handled. +29. Get the skillprovider from the postman. + +30. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"userattribute","userId":"391a3656-9a01-47d4-8c6d-64b68c44f212","attributeId":"b5a50f73-08e2-43d1-a78a-4652f15d950e","value":"userattribute"}}` +31. Watch the app console, It will show message successfully handled. +32. Get the userattribute from the postman. + +33. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"userrole","userId":"391a3656-9a01-47d4-8c6d-64b68c44f212","roleId":"188446f1-02dc-4fc7-b74e-ab7ea3033a57"}}` +34. Watch the app console, It will show message successfully handled. +35. Get the userrole from the postman. + +36. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"userskill","userId":"391a3656-9a01-47d4-8c6d-64b68c44f212","skillId":"8a8c8d3a-9165-4dae-8a8c-f828cbe01d5d","metricValue":"userskill","certifierId":"7cf786d9-a8c0-48ed-a7cc-09dcf91d904c","certifiedDate":"2019-07-08T00:00:00.000Z"}}` +37. Watch the app console, It will show message successfully handled. +38. Get the userskill from the postman. + +39. write message: + `{"topic":"u-bahn.action.create","originator":"u-bahn-api","timestamp":"2019-07-08T00:00:00.000Z","mime-type":"application/json","payload":{"resource":"attribute","id":"b5a50f73-08e2-43d1-a78a-4652f15d950e","name":"attribute", "attributeGroupId": "720c34f9-0fd4-46fd-9293-4a8cfdcd3e96"}}` +40. Watch the app console, It will show message successfully handled. +41. Get the attribute from the postman. + +42. These are full set of data for one user. +Get users with enrich=true, verify all data above are returned + +43. Verify query parameters in each resource are works based on the swagger doc. +When setting query parameter to the non-existing value, verify the app tries to get the data from QLDB in the console. + +44. By changing id values from above data, create another users or other resources. + +45. Test paging, query filtering with multiple resources. + +46. Please verify all specifications in the project. diff --git a/app.js b/app.js new file mode 100755 index 0000000..39be486 --- /dev/null +++ b/app.js @@ -0,0 +1,115 @@ +/** + * The application entry point + */ + +require('./src/bootstrap') +const config = require('config') +const express = require('express') +const cross = require('cors') +const bodyParser = require('body-parser') +const _ = require('lodash') +const http = require('http') +const swaggerUi = require('swagger-ui-express') +const jsyaml = require('js-yaml') +const fs = require('fs') +const path = require('path') +const logger = require('./src/common/logger') +const errorMiddleware = require('./src/common/error.middleware') +const routes = require('./src/route') +const authenticator = require('tc-core-library-js').middleware.jwtAuthenticator +const errors = require('./src/common/errors') +const app = express() +const httpServer = http.Server(app) +const { checkIfExists } = require('./src/common/helper') +const models = require('./src/models') + +app.set('port', config.PORT) +app.use(bodyParser.json()) +app.use(bodyParser.urlencoded({ extended: true })) +app.use(cross()) +const apiRouter = express.Router({}) + +// load all routes +_.each(routes, (verbs, url) => { + _.each(verbs, (def, verb) => { + const actions = [] + + const { method } = def + if (!method) { + throw new Error(`${verb.toUpperCase()} ${url} method is undefined`) + } + let access = [] + // Authentication and Authorization + if (def.auth) { + // default access roles + access = def.access || [] + actions.push((req, res, next) => { + authenticator(_.pick(config, ['AUTH_SECRET', 'VALID_ISSUERS']))(req, res, next) + }) + actions.push((req, res, next) => { + if (!req.authUser) { + return next(errors.newAuthError('Action is not allowed for invalid token')) + } + req.auth = req.authUser + req.auth.sub = req.auth.userId + if (req.authUser.roles) { + // all access are allowed + if (_.isEmpty(access)) { + next() + } else if (!checkIfExists(access, req.authUser.roles)) { + res.forbidden = true + next(errors.newPermissionError('You are not allowed to perform this action')) + } else { + next() + } + } else if (req.authUser.scopes) { + if (_.isNil(def.scopes) || _.isEmpty(def.scopes)) { + next() + } else if (!checkIfExists(def.scopes, req.authUser.scopes)) { + next(errors.newPermissionError('You are not allowed to perform this action!')) + } else { + next() + } + } else if ((_.isArray(def.access) && def.access.length > 0) || (_.isArray(def.scopes) && def.scopes.length > 0)) { + next(errors.newAuthError('You are not authorized to perform this action')) + } else { + next() + } + }) + } + + actions.push(async (req, res, next) => { + try { + await method(req, res, next) + } catch (e) { + next(e) + } + }) + + logger.info(`Endpoint discovered : [${access}] ${verb.toLocaleUpperCase()} /${config.API_VERSION}${url}`) + apiRouter[verb](`/${config.API_VERSION}${url}`, actions) + }) +}) +app.use('/', apiRouter) +const spec = fs.readFileSync(path.join(__dirname, 'docs/swagger.yaml'), 'utf8') +const swaggerDoc = jsyaml.safeLoad(spec) + +app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc)) + +app.use(errorMiddleware()) +app.use('*', (req, res) => { + const pathKey = req.baseUrl.substring(config.API_VERSION.length + 1) + const route = routes[pathKey] + if (route) { + res.status(405).json({ message: 'The requested method is not supported.' }) + } else { + res.status(404).json({ message: 'The requested resource cannot found.' }) + } +}); + +(async () => { + await models.init() + httpServer.listen(app.get('port'), () => { + logger.info(`Express server listening on port ${app.get('port')}`) + }) +})() diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b2362f3 --- /dev/null +++ b/build.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -eo pipefail +APP_NAME=$1 +UPDATE_CACHE="" +echo "" > docker/api.env +docker-compose -f docker/docker-compose.yml build $APP_NAME +docker create --name app $APP_NAME:latest + +if [ -d node_modules ] +then + mv package-lock.json old-package-lock.json + docker cp app:/$APP_NAME/package-lock.json package-lock.json + set +eo pipefail + UPDATE_CACHE=$(cmp package-lock.json old-package-lock.json) + set -eo pipefail +else + UPDATE_CACHE=1 +fi + +if [ "$UPDATE_CACHE" == 1 ] +then + docker cp app:/$APP_NAME/node_modules . +fi \ No newline at end of file diff --git a/config/default.js b/config/default.js new file mode 100755 index 0000000..af863d0 --- /dev/null +++ b/config/default.js @@ -0,0 +1,110 @@ +/** + * the default config + */ + +module.exports = { + LOG_LEVEL: process.env.LOG_LEVEL || 'debug', + PORT: process.env.PORT || 3001, + + AUTH_SECRET: process.env.AUTH_SECRET || 'CLIENT_SECRET', + VALID_ISSUERS: process.env.VALID_ISSUERS ? process.env.VALID_ISSUERS.replace(/\\"/g, '') + : '["https://topcoder-dev.auth0.com/", "https://api.topcoder.com"]', + + PAGE_SIZE: process.env.PAGE_SIZE || 20, + API_VERSION: process.env.API_VERSION || 'api/1.0', + + AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, + AWS_REGION: process.env.AWS_REGION || 'us-east-1', + DATABASE: process.env.DATABASE || 'ubahn-db', + + AUTH0_URL: process.env.AUTH0_URL, + AUTH0_AUDIENCE: process.env.AUTH0_AUDIENCE, + TOKEN_CACHE_TIME: process.env.TOKEN_CACHE_TIME, + AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID, + AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET, + AUTH0_PROXY_SERVER_URL: process.env.AUTH0_PROXY_SERVER_URL, + + GROUP_API_URL: process.env.GROUP_API_URL || 'https://api.topcoder-dev.com/v5/groups', + BUSAPI_URL: process.env.BUSAPI_URL || 'https://api.topcoder-dev.com/v5', + + KAFKA_ERROR_TOPIC: process.env.KAFKA_ERROR_TOPIC || 'common.error.reporting', + KAFKA_MESSAGE_ORIGINATOR: process.env.KAFKA_MESSAGE_ORIGINATOR || 'u-bahn-api', + + // topics + UBAHN_CREATE_TOPIC: process.env.UBAHN_CREATE_TOPIC || 'u-bahn.action.create', + UBAHN_UPDATE_TOPIC: process.env.UBAHN_UPDATE_TOPIC || 'u-bahn.action.update', + UBAHN_DELETE_TOPIC: process.env.UBAHN_DELETE_TOPIC || 'u-bahn.action.delete', + + EMSI: { + CLIENT_ID: process.env.EMSI_CLIENT_ID, + CLIENT_SECRET: process.env.EMSI_CLIENT_SECRET, + GRANT_TYPE: process.env.EMSI_GRANT_TYPE || 'client_credentials', + SCOPE: process.env.EMSI_SCOPE || 'emsi_open', + AUTH_URL: process.env.EMSI_AUTH_URL || 'https://auth.emsicloud.com/connect/token', + BASE_URL: process.env.EMSI_BASE_URL || 'https://skills.emsicloud.com/versions/latest' + }, + + EMSI_SKILLPROVIDER_ID: process.env.EMSI_SKILLPROVIDER_ID || '7637ae1a-3b7c-44eb-a5ed-10ea02f1885d', + + // ElasticSearch + ES: { + HOST: process.env.ES_HOST || 'localhost:9200', + API_VERSION: process.env.ES_API_VERSION || '7.4', + // es mapping: _index, _type, _id + DOCUMENTS: { + achievementprovider: { + index: process.env.ACHIEVEMENT_PROVIDER_INDEX || 'achievement_provider', + type: '_doc' + }, + attribute: { + index: process.env.ATTRIBUTE_INDEX || 'attribute', + type: '_doc' + }, + attributegroup: { + index: process.env.ATTRIBUTE_GROUP_INDEX || 'attribute_group', + type: '_doc' + }, + organization: { + index: process.env.ORGANIZATION_INDEX || 'organization', + type: '_doc' + }, + role: { + index: process.env.ROLE_INDEX || 'role', + type: '_doc' + }, + skill: { + index: process.env.SKILL_INDEX || 'skill', + type: '_doc' + }, + skillprovider: { + index: process.env.SKILL_PROVIDER_INDEX || 'skill_provider', + type: '_doc' + }, + user: { + index: process.env.USER_INDEX || 'user', + type: '_doc' + }, + // sub resources under user + achievement: { + userField: process.env.USER_ACHIEVEMENT_PROPERTY_NAME || 'achievements' + }, + externalprofile: { + userField: process.env.USER_EXTERNALPROFILE_PROPERTY_NAME || 'externalProfiles' + }, + userattribute: { + userField: process.env.USER_ATTRIBUTE_PROPERTY_NAME || 'attributes' + }, + userrole: { + userField: process.env.USER_ROLE_PROPERTY_NAME || 'roles' + }, + userskill: { + userField: process.env.USER_SKILL_PROPERTY_NAME || 'skills' + }, + // sub resources under organization + organizationskillprovider: { + orgField: process.env.ORGANIZATION_SKILLPROVIDER_PROPERTY_NAME || 'skillProviders' + } + } + } +} diff --git a/config/production.js b/config/production.js new file mode 100755 index 0000000..00b345f --- /dev/null +++ b/config/production.js @@ -0,0 +1,7 @@ +/** + * The production configuration file. + */ + +module.exports = { + LOG_LEVEL: process.env.LOG_LEVEL || 'info' +} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..1a27218 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,12 @@ +# Use the base image with Node.js 12 +FROM node:12 + +# Copy the current directory into the Docker image +COPY . /ubahn_api + +# Set working directory for future use +WORKDIR /ubahn_api + +# Install the dependencies from package.json +RUN npm install +CMD npm start diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..de4804b --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + ubahn_api: + image: ubahn_api:latest + build: + context: ../ + dockerfile: docker/Dockerfile + env_file: + - api.env + ports: + - "3001:3001" diff --git a/docker/sample.api.env b/docker/sample.api.env new file mode 100644 index 0000000..14ff185 --- /dev/null +++ b/docker/sample.api.env @@ -0,0 +1,9 @@ +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +ES_HOST= + +AUTH0_URL= +AUTH0_AUDIENCE= +TOKEN_CACHE_TIME=500000 +AUTH0_CLIENT_ID= +AUTH0_CLIENT_SECRET= diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json new file mode 100644 index 0000000..e86ffe3 --- /dev/null +++ b/docs/UBahn_API.postman_collection.json @@ -0,0 +1,3691 @@ +{ + "info": { + "_postman_id": "2e9d0d1e-22b4-4b5c-af7d-8e5d407de79b", + "name": "UBahn_API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "search", + "item": [ + { + "name": "{{HOST}}/search/userAttributes", + "event": [ + { + "listen": "test", + "script": { + "id": "256b27ab-e629-429f-ad93-372eaaa46239", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/search/userAttributes?attributeId=c44d4bee-1356-46d6-9f1f-991936dec297&attributeValue=support", + "host": [ + "{{HOST}}" + ], + "path": [ + "search", + "userAttributes" + ], + "query": [ + { + "key": "attributeId", + "value": "c44d4bee-1356-46d6-9f1f-991936dec297" + }, + { + "key": "attributeValue", + "value": "support" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/search/userAchievements", + "event": [ + { + "listen": "test", + "script": { + "id": "d1951826-476c-4149-bfa7-2a853c30df4e", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/search/userAchievements?organizationId=36ed815b-3da1-49f1-a043-aaed0a4e81ad&keyword=Topcoder", + "host": [ + "{{HOST}}" + ], + "path": [ + "search", + "userAchievements" + ], + "query": [ + { + "key": "organizationId", + "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad" + }, + { + "key": "keyword", + "value": "Topcoder" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/search/skills", + "event": [ + { + "listen": "test", + "script": { + "id": "d56349bd-a797-47cf-b6bf-2ecfbd342815", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/search/skills?organizationId=36ed815b-3da1-49f1-a043-aaed0a4e81ad&keyword=net", + "host": [ + "{{HOST}}" + ], + "path": [ + "search", + "skills" + ], + "query": [ + { + "key": "organizationId", + "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad" + }, + { + "key": "keyword", + "value": "net" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/search/users", + "event": [ + { + "listen": "test", + "script": { + "id": "75560f72-efeb-438e-ac29-24d7c6ab540e", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"isAvailable\": \"false\",\n \"locations\": [\"New York\", \"London\"],\n \"skills\": [\"Angular (Web Framework)\", \"Python (Programming Language)\"],\n \"achievements\": [\"Upwork\", \"Topcoder\"],\n \"attributes\": [{\n \"id\": \"c44d4bee-1356-46d6-9f1f-991936dec297\",\n \"value\": [\"Senior Consultant\", \"IT Support Specialist\", \"Consultant\"]\n }, {\n \"id\": \"f3fd623f-a613-4e3c-bf2f-9df529ff4317\",\n \"value\": [\"Bulwark International Inc\"]\n }]\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/search/users", + "host": [ + "{{HOST}}" + ], + "path": [ + "search", + "users" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "users", + "item": [ + { + "name": "{{HOST}}/users", + "event": [ + { + "listen": "test", + "script": { + "id": "5db9dc16-daad-4768-a3af-ea8983487bec", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"handle\":\"handle_01\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}?enrich=true", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ], + "query": [ + { + "key": "enrich", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"handle\":\"handle_05\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users?enrich=true&role.name=role-2", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ], + "query": [ + { + "key": "enrich", + "value": "true" + }, + { + "key": "roleId", + "value": "8607ddb3-abf6-4512-a618-c60d4771174b", + "disabled": true + }, + { + "key": "userSkill.skillId", + "value": "8a8c8d3a-9165-4dae-8a8c-f828cbe01d5d", + "disabled": true + }, + { + "key": "userSkill.metricValue", + "value": "userskill", + "disabled": true + }, + { + "key": "skill.name", + "value": "userskill", + "disabled": true + }, + { + "key": "skillProvider.name", + "value": "skillprovider-2", + "disabled": true + }, + { + "key": "userrole.roleId", + "value": "bbec5193-bef2-4907-93bc-3e82cbf81dc0", + "disabled": true + }, + { + "key": "role.name", + "value": "role-2" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users Search (enrich)", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users?enrich=true&skill=React&skill=skill_name_update&achievement=achievement-name-01&achievement=string&location=London&location=New York&isAvailable=true", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ], + "query": [ + { + "key": "enrich", + "value": "true" + }, + { + "key": "roleId", + "value": "8607ddb3-abf6-4512-a618-c60d4771174b", + "disabled": true + }, + { + "key": "userSkill.skillId", + "value": "8a8c8d3a-9165-4dae-8a8c-f828cbe01d5d", + "disabled": true + }, + { + "key": "userSkill.metricValue", + "value": "userskill", + "disabled": true + }, + { + "key": "skill.name", + "value": "userskill", + "disabled": true + }, + { + "key": "skillProvider.name", + "value": "skillprovider-2", + "disabled": true + }, + { + "key": "userrole.roleId", + "value": "bbec5193-bef2-4907-93bc-3e82cbf81dc0", + "disabled": true + }, + { + "key": "role.name", + "value": "role-2", + "disabled": true + }, + { + "key": "skill", + "value": "React" + }, + { + "key": "skill", + "value": "skill_name_update" + }, + { + "key": "achievement", + "value": "achievement-name-01" + }, + { + "key": "achievement", + "value": "string" + }, + { + "key": "location", + "value": "London" + }, + { + "key": "location", + "value": "New York" + }, + { + "key": "isAvailable", + "value": "true" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "roles", + "item": [ + { + "name": "{{HOST}}/roles", + "event": [ + { + "listen": "test", + "script": { + "id": "8469c20d-3ef5-4868-af50-d603584ed8a7", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"roleId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"Admin\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"Admin02\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/roles?name=m", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles" + ], + "query": [ + { + "key": "name", + "value": "m" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/roles/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "usersRoles", + "item": [ + { + "name": "{{HOST}}/users/:userId/roles", + "event": [ + { + "listen": "test", + "script": { + "id": "d4ca59b9-f350-4c91-9667-a5e749eed956", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"roleId\":\"{{roleId}}\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles/:roleId", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles/:roleId", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"roleId\":\"8607ddb3-abf6-4512-a618-c60d4771174b\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles/:roleId", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/roles/:roleId", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "organizations", + "item": [ + { + "name": "{{HOST}}/organizations", + "event": [ + { + "listen": "test", + "script": { + "id": "ff10985c-75c8-48ba-b522-2c8213b0c6cd", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"organizationId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"organization_01\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/organizations", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/organizations/603d4264-cdb0-47f1-914e-f053abc60422", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "603d4264-cdb0-47f1-914e-f053abc60422" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"organization_update\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations?name=o", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations" + ], + "query": [ + { + "key": "name", + "value": "o" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "skillsProviders", + "item": [ + { + "name": "{{HOST}}/skillsProviders", + "event": [ + { + "listen": "test", + "script": { + "id": "0a875ba8-b4dc-42cc-b8ba-deb560b73556", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"skillsProviderId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"skillsProviders_01\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skillsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"skillsProviders_update\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/skillsProviders?name=ski", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders" + ], + "query": [ + { + "key": "name", + "value": "ski" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/skillsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skillsProviders/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "skills", + "item": [ + { + "name": "{{HOST}}/skills", + "event": [ + { + "listen": "test", + "script": { + "id": "909e2488-3801-4b57-90c5-6a6ae2e91f13", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"skillId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"skillProviderId\":\"{{skillsProviderId}}\",\n\t\"name\":\"jump\",\n\t\"uri\":\"http://www.google.com\",\n\t\"externalId\":\"externalId\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"skill_name_update\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills" + ], + "query": [ + { + "key": "perPage", + "value": "2", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/skills/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "usersSkills", + "item": [ + { + "name": "{{HOST}}/users/:userId/skills", + "event": [ + { + "listen": "test", + "script": { + "id": "e3241acb-646f-4ec8-83fb-8a3d63da4990", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"skillId\":\"{{skillId}}\",\n\t\"metricValue\":\"3L\",\n\t\"certifierId\":\"certifier_id\",\n\t\"certifiedDate\":\"2020-05-04T07:36:28.036Z\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills/:skillId", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills/:skillId", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"metricValue\":\"4.5L\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills/:skillId", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills?skillName=skill-2", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ], + "query": [ + { + "key": "skillName", + "value": "skill-2" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/skills/:skillId", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "externalProfiles", + "item": [ + { + "name": "{{HOST}}/users/:userId/externalProfiles", + "event": [ + { + "listen": "test", + "script": { + "id": "ee9256b0-cdc2-4194-9da7-c0b363082385", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"uri\":\"http://uri.com/uri\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles/:organizationId", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles/:organizationId", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"uri\":\"http://www.new.com/new-uri\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles/:organizationId", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles?organizationName=organization", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ], + "query": [ + { + "key": "organizationName", + "value": "organization" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/externalProfiles/:organizationId", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "achievementsProviders", + "item": [ + { + "name": "{{HOST}}/achievementsProviders", + "event": [ + { + "listen": "test", + "script": { + "id": "3c959e64-93e9-480d-b92e-44eacb46c2b1", + "exec": [ + "var rsp = pm.response.json();", + "if(rsp.id) pm.environment.set(\"achievementsProviderId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"achievementsProviders_02\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/achievementsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"achievementsProviders_update\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/achievementsProviders?name=a", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders" + ], + "query": [ + { + "key": "name", + "value": "a" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/achievementsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/achievementsProviders/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "achievements", + "item": [ + { + "name": "{{HOST}}/users/:userId/achievements", + "event": [ + { + "listen": "test", + "script": { + "id": "9b4f70b8-3ca8-4c1c-a65a-8a8b37e2fa62", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"achievementsProviderId\":\"{{achievementsProviderId}}\",\n\t\"name\":\"achievement-name-01\",\n\t\"uri\":\"http://www.google.com/xx\",\n\t\"certifierId\":\"certifierId\",\n\t\"certifiedDate\":\"2020-05-04T07:36:28.036Z\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/achievements/:achievementsProviderId", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/7b4f98b1-5831-45fe-a71f-8454d11eb8e8", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "7b4f98b1-5831-45fe-a71f-8454d11eb8e8" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/achievements/:achievementsProviderId", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t \"name\": \"string\",\n\t \"uri\": \"string\",\n\t \"certifierId\": \"string\",\n\t \"certifiedDate\": \"2020-05-13T06:33:54.708Z\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/achievements/:achievementsProviderId", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "{{achievementsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/achievements", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements" + ], + "query": [ + { + "key": "achievementsProviderName", + "value": "achie", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/achievements", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/achievements/:achievementsProviderId", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "attributeGroups", + "item": [ + { + "name": "{{HOST}}/attributeGroups", + "event": [ + { + "listen": "test", + "script": { + "id": "fb796928-9848-47a3-be88-61a8fc9e60f7", + "exec": [ + "var rsp = pm.response.json();", + "if (rsp.id) pm.environment.set(\"attributeGroupId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"name\":\"attributeGroup_01\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributeGroups", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"group 03\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/attributeGroups", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ], + "query": [ + { + "key": "name", + "value": "attributegroup", + "disabled": true + }, + { + "key": "organizationId", + "value": "603d4264-cdb0-47f1-914e-f053abc60422", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/attributeGroups", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributeGroups/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "attributes", + "item": [ + { + "name": "{{HOST}}/attributes", + "event": [ + { + "listen": "test", + "script": { + "id": "eb028137-ee03-496c-a812-2966380bca42", + "exec": [ + "var rsp = pm.response.json();", + "if (rsp.id) pm.environment.set(\"attributeId\", rsp.id);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"attributeGroupId\":\"{{attributeGroupId}}\",\n\t\"name\":\"attribute_02\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes/:id", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes/:id", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\":\"attr-04\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes/:id", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/attributes?name=attr", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes" + ], + "query": [ + { + "key": "name", + "value": "attr" + }, + { + "key": "attributeGroupId", + "value": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/attributes/:id", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "userAttributes", + "item": [ + { + "name": "{{HOST}}/users/:userId/attributes", + "event": [ + { + "listen": "test", + "script": { + "id": "06c18b73-b9e6-46a8-acfa-74fc7ae68ac5", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"attributeId\":\"{{attributeId}}\",\n\t\"value\":\"1.23\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/attributes/:attributeId", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/b746ef65-336d-4846-b02a-f25f6cff72c9", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "b746ef65-336d-4846-b02a-f25f6cff72c9" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/attributes/:attributeId", + "request": { + "method": "PATCH", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"value\":\"2.56\"\n}", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/attributes/:attributeId", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/attributes", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes?attributeName=attribute&attributeGroupName=attributegroup&attributeGroupId=720c34f9-0fd4-46fd-9293-4a8cfdcd3e96", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ], + "query": [ + { + "key": "attributeName", + "value": "attribute" + }, + { + "key": "attributeGroupName", + "value": "attributegroup" + }, + { + "key": "attributeGroupId", + "value": "720c34f9-0fd4-46fd-9293-4a8cfdcd3e96" + } + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/:userId/attributes", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/users/{{userId}}/attributes/:attributeId", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": {} + } + }, + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "Group API", + "item": [ + { + "name": "{{HOST}}/users/:userId/attributes Copy", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "type": "text", + "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoibWFFMm1hQlN2OWZSVkhqU2xDMzFMRlpTcTZWaGhacUNAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTkwNDczODY5LCJleHAiOjE1OTA1NjAyNjksImF6cCI6Im1hRTJtYUJTdjlmUlZIalNsQzMxTEZaU3E2VmhoWnFDIiwic2NvcGUiOiJyZWFkOmNoYWxsZW5nZXMgd3JpdGU6Y2hhbGxlbmdlcyByZWFkOmdyb3VwcyB1cGRhdGU6c3VibWlzc2lvbiByZWFkOnN1Ym1pc3Npb24gZGVsZXRlOnN1Ym1pc3Npb24gY3JlYXRlOnN1Ym1pc3Npb24gYWxsOnN1Ym1pc3Npb24gdXBkYXRlOnJldmlld190eXBlIHJlYWQ6cmV2aWV3X3R5cGUgZGVsZXRlOnJldmlld190eXBlIGFsbDpyZXZpZXdfdHlwZSB1cGRhdGU6cmV2aWV3X3N1bW1hdGlvbiByZWFkOnJldmlld19zdW1tYXRpb24gZGVsZXRlOnJldmlld19zdW1tYXRpb24gY3JlYXRlOnJldmlld19zdW1tYXRpb24gYWxsOnJldmlld19zdW1tYXRpb24gdXBkYXRlOnJldmlldyByZWFkOnJldmlldyBkZWxldGU6cmV2aWV3IGNyZWF0ZTpyZXZpZXcgYWxsOnJldmlldyByZWFkOmJ1c190b3BpY3Mgd3JpdGU6YnVzX2FwaSByZWFkOnVzZXJfcHJvZmlsZXMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.MAhr5GZaOsx5nmkE1wY4QQP2TQWzegw64TJHfth1-ShRJpy7bPG5LPCmY4YDWphi7K4IrjyKril8ZY9BdbDKnpzXbviZvEZzO8eAXgPlemmCoLik8x_DaZjYJ1CjJlEzLIkh4vtJ-H5lxYT36KTTOzN6z6nkvVk8kql2gEMlXzTTipQre8BBtZMyIvegHamYufBZqIO526JxLeCcv77z-0QtCN7uI5KaquJnRt4ijr9Tm72RNDXd_YmlEYVDhTd3brg-KZ8Dtq4BrMKvpru_omqDZeXKZMU4yK5tmAiJ-9VXYbcmMhsqRzJGfIpO-37-pFnYE34J6EGqnoIVmcQTdw" + } + ], + "url": { + "raw": "https://api.topcoder-dev.com/v5/groups", + "protocol": "https", + "host": [ + "api", + "topcoder-dev", + "com" + ], + "path": [ + "v5", + "groups" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "organizationSkillsProvider", + "item": [ + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"skillProviderId\": \"{{skillsProviderId}}\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "HEAD", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + }, + { + "name": "{{HOST}}/organizations/{{organizationId}}/skillProviders", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}/skillProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}", + "skillProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/docs/UBahn_ENV.postman_environment.json b/docs/UBahn_ENV.postman_environment.json new file mode 100644 index 0000000..6645a14 --- /dev/null +++ b/docs/UBahn_ENV.postman_environment.json @@ -0,0 +1,44 @@ +{ + "id": "1f07c3cc-5af9-4dc1-b038-129aaf1fac6e", + "name": "UBahn_ENV", + "values": [ + { + "key": "HOST", + "value": "http://127.0.0.1:3002/api/1.0", + "enabled": true + }, + { + "key": "token", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29waWxvdCIsIkFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLUFkbWluIiwiZXhwIjoxNjg1NTcxNDYwLCJ1c2VySWQiOiIyMzE2Njc2OCIsImlhdCI6MTU4NTU3MDg2MCwiZW1haWwiOiJ0Yy1BZG1pbkBnbWFpbC5jb20iLCJqdGkiOiIwZjFlZjFkMy0yYjMzLTQ5MDAtYmI0My00OGYyMjg1Zjk2MzAifQ.D_TtClF4xkuSPSWoUYvkWigUWVFhH5UuF7Eci4S1_xg", + "enabled": true + }, + { + "key": "userId", + "value": "55f6fa1c-fc38-4b74-83ad-278babd7efd2", + "enabled": true + }, + { + "key": "roleId", + "value": "", + "enabled": true + }, + { + "key": "skillsProviderId", + "value": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "enabled": true + }, + { + "key": "skillId", + "value": "e72e432f-6ef4-4f19-962b-eb59b805b317", + "enabled": true + }, + { + "key": "organizationId", + "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2020-08-02T07:47:16.803Z", + "_postman_exported_using": "Postman/7.29.1" +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..961d29f --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,4057 @@ +--- +swagger: "2.0" +info: + description: "API for an employee management system to determine employees that\ + \ are no longer working on active projects and to understand their qualifications\ + \ and expertise for suitability in other projects" + version: "1.0.0" + title: "UBahn API" +host: "ubahn-api-dev.herokuapp.com" +basePath: "/v5" +tags: +- name: "Users" + description: "Users registered in the system" +- name: "Users Skills" + description: "Skills of users" +- name: "Skills" + description: "Skills registered in the system" +- name: "Skills Provider" + description: "Skill providers registered in the system" +- name: "Roles" + description: "Roles registered in the system" +- name: "User Roles" + description: "Roles of users" +- name: "External Profiles" + description: "External profiles of users w.r.t an organization" +- name: "Achievements" + description: "Achievements of users" +- name: "Achievements Provider" + description: "Achievement providers registered in the system" +- name: "Organizations" + description: "Organizations registered in the system" +- name: "User Attributes" + description: "Attributes of users" +- name: "Attributes" + description: "Attributes registered in the system" +- name: "Attribute Groups" + description: "Attribute groups registered in the system" +schemes: +- "https" +consumes: +- "application/json" +produces: +- "application/json" +paths: + /users: + get: + tags: + - "Users" + description: "**Point to note** - For non-admin users, this endpoint will only\ + \ return entities that the user has created." + operationId: "usersGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "handle" + in: "query" + description: "Filter by user handle" + required: false + type: "string" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + - name: "skill[]" + in: "query" + type: "array" + description: "The skill name to filter users with. Specify multiple times to provide multiple values" + items: + type: "string" + collectionFormat: multi + - name: "achievement[]" + in: "query" + type: "array" + description: "The achievement name to filter users with. Specify multiple times to provide multiple values" + items: + type: "string" + collectionFormat: multi + - name: "location[]" + in: "query" + type: "array" + description: "The location name to filter users with. Specify multiple times to provide multiple values. `location` here is an attribute of the user" + items: + type: "string" + collectionFormat: multi + - name: "isAvailable" + in: "query" + type: "boolean" + description: "`true` corresponds to users that are available. `false` corresponds to users that are not available. `isAvailable` here is an attribute of the user" + items: + type: "string" + collectionFormat: multi + - name: "orderBy" + in: "query" + type: "string" + description: "Sort order" + enum: ["name", "location", "isAvailable"] + - name: "roleId" + in: "query" + description: "Filter by user roleId" + required: false + type: "string" + format: "UUID" + - name: "userSkill.skillId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userSkill.metricValue" + in: "query" + required: false + type: "string" + - name: "userSkill.certifierId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userSkill.certifiedDate" + in: "query" + required: false + type: "string" + - name: "skill.skillProviderId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "skill.name" + in: "query" + required: false + type: "string" + - name: "skill.externalId" + in: "query" + required: false + type: "string" + - name: "skill.uri" + in: "query" + required: false + type: "string" + - name: "skillProvider.name" + in: "query" + required: false + type: "string" + - name: "userRole.roleId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "role.name" + in: "query" + required: false + type: "string" + - name: "externalProfile.organizationId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "externalProfile.uri" + in: "query" + required: false + type: "string" + - name: "organization.name" + in: "query" + required: false + type: "string" + - name: "achievement.achievementProviderId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "achievement.name" + in: "query" + required: false + type: "string" + - name: "achievement.uri" + in: "query" + required: false + type: "string" + - name: "achievement.certifierId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "achievement.certifiedDate" + in: "query" + required: false + type: "string" + - name: "achievementProvider.name" + in: "query" + required: false + type: "string" + - name: "userAttribute.attributeId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userAttribute.value" + in: "query" + required: false + type: "string" + - name: "attribute.attributeGroupId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "attribute.name" + in: "query" + required: false + type: "string" + - name: "attributeGroup.name" + in: "query" + required: false + type: "string" + - name: "attributeGroup.organizationId" + in: "query" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/User" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + head: + tags: + - "Users" + description: "Retrieve header information for a search operation on users in\ + \ the application.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "usersHEAD" + parameters: + - name: "handle" + in: "query" + description: "Filter by user handle" + required: false + type: "string" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + - name: "skill[]" + in: "query" + type: "array" + description: "The skill name to filter users with. Specify multiple times to provide multiple values" + items: + type: "string" + collectionFormat: multi + - name: "achievement[]" + in: "query" + type: "array" + description: "The achievement name to filter users with. Specify multiple times to provide multiple values" + items: + type: "string" + collectionFormat: multi + - name: "location[]" + in: "query" + type: "array" + description: "The location name to filter users with. Specify multiple times to provide multiple values. `location` here is an attribute of the user" + items: + type: "string" + collectionFormat: multi + - name: "isAvailable" + in: "query" + type: "boolean" + description: "`true` corresponds to users that are available. `false` corresponds to users that are not available. `isAvailable` here is an attribute of the user" + items: + type: "string" + collectionFormat: multi + - name: "orderBy" + in: "query" + type: "string" + description: "Sort order" + enum: ["name", "location", "isAvailable"] + - name: "roleId" + in: "query" + description: "Filter by user roleId" + required: false + type: "string" + format: "UUID" + - name: "userSkill.skillId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userSkill.metricValue" + in: "query" + required: false + type: "string" + - name: "userSkill.certifierId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userSkill.certifiedDate" + in: "query" + required: false + type: "string" + - name: "skill.skillProviderId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "skill.name" + in: "query" + required: false + type: "string" + - name: "skill.externalId" + in: "query" + required: false + type: "string" + - name: "skill.uri" + in: "query" + required: false + type: "string" + - name: "skillProvider.name" + in: "query" + required: false + type: "string" + - name: "userRole.roleId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "role.name" + in: "query" + required: false + type: "string" + - name: "externalProfile.organizationId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "externalProfile.uri" + in: "query" + required: false + type: "string" + - name: "organization.name" + in: "query" + required: false + type: "string" + - name: "achievement.achievementProviderId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "achievement.name" + in: "query" + required: false + type: "string" + - name: "achievement.uri" + in: "query" + required: false + type: "string" + - name: "achievement.certifierId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "achievement.certifiedDate" + in: "query" + required: false + type: "string" + - name: "achievementProvider.name" + in: "query" + required: false + type: "string" + - name: "userAttribute.attributeId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "userAttribute.value" + in: "query" + required: false + type: "string" + - name: "attribute.attributeGroupId" + in: "query" + required: false + type: "string" + format: "UUID" + - name: "attribute.name" + in: "query" + required: false + type: "string" + - name: "attributeGroup.name" + in: "query" + required: false + type: "string" + - name: "attributeGroup.organizationId" + in: "query" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + post: + tags: + - "Users" + description: "Create a new User.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "usersPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/User" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + /users/{userId}: + get: + tags: + - "Users" + description: "Get User with given id.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\ + \ \n" + operationId: "usersUserIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/User" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + head: + tags: + - "Users" + description: "Get User with given id, but only header information is returned.\n\ + \n**Security** - Note that for non-admin users, this endpoint will only return\ + \ entities that\nthe user has created. \n" + operationId: "usersUserIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + delete: + tags: + - "Users" + description: "Remove an existing User with given id.\n\n**Security** - Note\ + \ that this endpoint is only available for admin users. \n" + operationId: "usersUserIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + patch: + tags: + - "Users" + description: "Update an existing User with given id.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only allow updates on entities\ + \ that the\ncalling user has created.\n" + operationId: "usersUserIdPATCH" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/User" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Users" + /users/{userId}/skills: + get: + tags: + - "Users Skills" + description: "Filter skills by its name given an user id. If no results, then\ + \ empty array is returned.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "usersUserIdSkillsGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillName" + in: "query" + description: "Filter by skill name (through skill id)" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/UserSkill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + head: + tags: + - "Users Skills" + description: "Retrieve header information for a search operation on users skills\ + \ in the application.\n\n**Security** - Note that for non-admin users, this\ + \ endpoint will only return entities that\nthe user has created.\n" + operationId: "usersUserIdSkillsHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillName" + in: "query" + description: "Filter by skill name (through skill id)" + required: false + type: "string" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + post: + tags: + - "Users Skills" + description: "Create a new User Skill.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "usersUserIdSkillsPOST" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserSkillRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserSkill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + /users/{userId}/skills/{skillId}: + get: + tags: + - "Users Skills" + description: "Get User Skills with given user and skill id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only return entities\ + \ that\nthe user has created. \n" + operationId: "usersUserIdSkillsSkillIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserSkill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + head: + tags: + - "Users Skills" + description: "Get User Skills with given ids, but only header information is\ + \ returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "usersUserIdSkillsSkillIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + delete: + tags: + - "Users Skills" + description: "Remove an existing User Skill with given ids.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "usersUserIdSkillsSkillIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + patch: + tags: + - "Users Skills" + description: "Update an existing skill with given ids.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only allow updates on entities\ + \ that the\ncalling user has created.\n" + operationId: "usersUserIdSkillsSkillIdPATCH" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserSkillUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserSkill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UsersSkills" + /skills: + get: + tags: + - "Skills" + description: "Get list of skills in the application. If no results, then empty\ + \ array is returned. Multiple filters are\nsupported.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only return entities that\n\ + the user has created.\n" + operationId: "skillsGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "skillProviderId" + in: "query" + description: "The referenced skill provider id" + type: "string" + format: "UUID" + - name: "externalId" + in: "query" + description: "The external id of the skill" + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Skill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + head: + tags: + - "Skills" + description: "Retrieve header information for get operation on Skills in the\ + \ application.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "skillsHEAD" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "skillProviderId" + in: "query" + description: "The referenced skill provider id" + type: "string" + format: "UUID" + - name: "externalId" + in: "query" + description: "The external id of the skill" + type: "string" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + post: + tags: + - "Skills" + description: "Create a new Skill.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "skillsPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/SkillRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Skill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + /skills/{skillId}: + get: + tags: + - "Skills" + description: "Get Skill by given skill id.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\ + \ \n" + operationId: "skillsSkillIdGET" + parameters: + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Skill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + head: + tags: + - "Skills" + description: "Retrieve header information for get operation on Skill by its\ + \ Id in the application.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created. \ + \ \n" + operationId: "skillsSkillIdHEAD" + parameters: + - name: "skillId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + delete: + tags: + - "Skills" + description: "Remove an existing skill with given id.\n\n**Security** - Note\ + \ that this endpoint is only available for admin users. \n" + operationId: "skillsSkillIdDELETE" + parameters: + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + patch: + tags: + - "Skills" + description: "Update an existing skill with given id.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only allow updates on entities\ + \ that the\ncalling user has created.\n" + operationId: "skillsSkillIdPATCH" + parameters: + - name: "skillId" + in: "path" + description: "The skill id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/SkillUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Skill" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Skills" + /skillsProviders: + get: + tags: + - "Skills Provider" + description: "Search Skills Provider in the application. If no results, then\ + \ empty array is returned.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "skillsProvidersGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "name" + in: "query" + description: "Filter by provider name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/SkillsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + head: + tags: + - "Skills Provider" + description: "Retrieve header information for a search operation on skills providers\ + \ in the application.\n\n**Security** - Note that for non-admin users, this\ + \ endpoint will only return entities that\nthe user has created.\n" + operationId: "skillsProvidersHEAD" + parameters: + - name: "name" + in: "query" + description: "Filter by provider name" + required: false + type: "string" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + post: + tags: + - "Skills Provider" + description: "Create a new Skills Provider.\n\n**Security** - This endpoint\ + \ is accessible by all authenticated users. \n" + operationId: "skillsProvidersPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/SkillsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + /skillsProviders/{providerId}: + get: + tags: + - "Skills Provider" + description: "Get skills provider with given id.\n\n**Security** - Note that\ + \ for non-admin users, this endpoint will only return entities that\nthe user\ + \ has created. \n" + operationId: "skillsProvidersProviderIdGET" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/SkillsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + head: + tags: + - "Skills Provider" + description: "Get skills provider with given id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "skillsProvidersProviderIdHEAD" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + delete: + tags: + - "Skills Provider" + description: "Remove an existing skills provider with given id.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "skillsProvidersProviderIdDELETE" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + patch: + tags: + - "Skills Provider" + description: "Update an existing skills provider with given id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "skillsProvidersProviderIdPATCH" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/SkillsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "SkillsProvider" + /roles: + get: + tags: + - "Roles" + description: "Search Roles in the application. If no results, then empty array\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "rolesGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "name" + in: "query" + description: "Filter by role name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Role" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + head: + tags: + - "Roles" + description: "Retrieve header information for a search operation on Roles in\ + \ the application.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "rolesHEAD" + parameters: + - name: "name" + in: "query" + description: "Filter by role name" + required: false + type: "string" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + post: + tags: + - "Roles" + description: "Create a new Role.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "rolesPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Role" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + /roles/{roleId}: + get: + tags: + - "Roles" + description: "Get role with given id.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\ + \ \n" + operationId: "rolesRoleIdGET" + parameters: + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Role" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + head: + tags: + - "Roles" + description: "Get role with given id, but only header information is returned.\n\ + \n**Security** - Note that for non-admin users, this endpoint will only return\ + \ entities that\nthe user has created. \n" + operationId: "rolesRoleIdHEAD" + parameters: + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + delete: + tags: + - "Roles" + description: "Remove an existing role with given id.\n\n**Security** - Note\ + \ that this endpoint is only available for admin users. \n" + operationId: "rolesRoleIdDELETE" + parameters: + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + patch: + tags: + - "Roles" + description: "Update an existing role with given id.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only allow updates on entities\ + \ that the\ncalling user has created.\n" + operationId: "rolesRoleIdPATCH" + parameters: + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Role" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Roles" + /users/{userId}/roles: + get: + tags: + - "User Roles" + description: "Get User Roles that belong to given user id.\n\n**Security** -\ + \ Note that for non-admin users, this endpoint will only return entities that\n\ + the user has created. \n" + operationId: "usersUserIdRolesGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/UserRole" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + head: + tags: + - "User Roles" + description: "Get User Roles that belong to given user id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "usersUserIdRolesHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + post: + tags: + - "User Roles" + description: "Create a new User Role.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "usersUserIdRolesPOST" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserRoleRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserRole" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + /users/{userId}/roles/{roleId}: + get: + tags: + - "User Roles" + description: "Get role by its id.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\n" + operationId: "usersUserIdRolesRoleIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/UserRole" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + head: + tags: + - "User Roles" + description: "Retrieve header information for a search operation on User Roles\ + \ in the application.\n\n**Security** - Note that for non-admin users, this\ + \ endpoint will only return entities that\nthe user has created.\n" + operationId: "usersUserIdRolesRoleIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + delete: + tags: + - "User Roles" + description: "Remove an existing user role with given user and role id.\n\n\ + **Security** - Note that this endpoint is only available for admin users.\ + \ \n" + operationId: "usersUserIdRolesRoleIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "roleId" + in: "path" + description: "The role id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserRoles" + /users/{userId}/externalProfiles: + get: + tags: + - "External Profiles" + description: "Get External Profiles with given user id.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only return entities that\n\ + the user has created. \n" + operationId: "usersUserIdExternalProfilesGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "organizationName" + in: "query" + description: "The organization name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/ExternalProfile" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + head: + tags: + - "External Profiles" + description: "Get External Profiles with given user id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "usersUserIdExternalProfilesHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + post: + tags: + - "External Profiles" + description: "Create a new External Profile for given user id.\n\n**Security**\ + \ - This endpoint is accessible by all authenticated users. \n" + operationId: "usersUserIdExternalProfilesPOST" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/ExternalProfileRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/ExternalProfile" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + /users/{userId}/externalProfiles/{organizationId}: + get: + tags: + - "External Profiles" + description: "Get external profile with given user id and organization id.\n\ + \n**Security** - Note that for non-admin users, this endpoint will only return\ + \ entities that\nthe user has created. \n" + operationId: "usersUserIdExternalProfilesOrganizationIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/ExternalProfile" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + head: + tags: + - "External Profiles" + description: "Get external profile with given user id and organization id, but\ + \ only header information is returned.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\ + \ \n" + operationId: "usersUserIdExternalProfilesOrganizationIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + delete: + tags: + - "External Profiles" + description: "Remove an existing external profile with given user id and organization\ + \ id.\n\n**Security** - Note that this endpoint is only available for admin\ + \ users. \n" + operationId: "usersUserIdExternalProfilesOrganizationIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + patch: + tags: + - "External Profiles" + description: "Update an existing external profile with given user id and organization\ + \ id.\n\n**Security** - Note that for non-admin users, this endpoint will\ + \ only allow updates on entities that the\ncalling user has created.\n" + operationId: "usersUserIdExternalProfilesOrganizationIdPATCH" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/ExternalProfileUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/ExternalProfile" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "ExternalProfiles" + /users/{userId}/achievements: + get: + tags: + - "Achievements" + description: "Get Achievements for given user id.\n\n**Security** - Note that\ + \ for non-admin users, this endpoint will only return entities that\nthe user\ + \ has created. \n" + operationId: "usersUserIdAchievementsGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsproviderName" + in: "query" + description: "The achievement provider name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Achievement" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + head: + tags: + - "Achievements" + description: "Get Achievements for given user id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "usersUserIdAchievementsHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsproviderName" + in: "query" + description: "The achievement provider name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + post: + tags: + - "Achievements" + description: "Create a new Achievement.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "usersUserIdAchievementsPOST" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AchievementRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Achievement" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + /users/{userId}/achievements/{achievementsProviderId}: + get: + tags: + - "Achievements" + description: "Get Achievements for given user id and provider id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only return entities\ + \ that\nthe user has created.\n" + operationId: "usersUserIdAchievementsAchievementsProviderIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsProviderId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Achievement" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + head: + tags: + - "Achievements" + description: "Get Achievements for given user id and provider id, but only header\ + \ information is returned.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created. \ + \ \n" + operationId: "usersUserIdAchievementsAchievementsProviderIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsProviderId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + delete: + tags: + - "Achievements" + description: "Remove an existing Achievement with given userId and achievement\ + \ providerId.\n\n**Security** - Note that this endpoint is only available\ + \ for admin users. \n" + operationId: "usersUserIdAchievementsAchievementsProviderIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsProviderId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + patch: + tags: + - "Achievements" + description: "Update an existing Achievement with given userId and achievement\ + \ providerId. Only the fields in the request body are updated.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "usersUserIdAchievementsAchievementsProviderIdPATCH" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "achievementsProviderId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AchievementUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Achievement" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Achievements" + /achievementsProviders: + get: + tags: + - "Achievements Provider" + description: "Search Achievements Provider in the application. If no results,\ + \ then empty array is returned.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only return entities that\nthe user has created.\n" + operationId: "achievementsProvidersGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "name" + in: "query" + description: "Filter by provider name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/AchievementsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + head: + tags: + - "Achievements Provider" + description: "Retrieve header information for a search operation on achivements\ + \ provider in the application.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "achievementsProvidersHEAD" + parameters: + - name: "name" + in: "query" + description: "Filter by provider name" + required: false + type: "string" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + post: + tags: + - "Achievements Provider" + description: "Create a new Achievements Provider.\n\n**Security** - This endpoint\ + \ is accessible by all authenticated users. \n" + operationId: "achievementsProvidersPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AchievementsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + /achievementsProviders/{providerId}: + get: + tags: + - "Achievements Provider" + description: "Get achievements provider with given id.\n\n**Security** - Note\ + \ that for non-admin users, this endpoint will only return entities that\n\ + the user has created. \n" + operationId: "achievementsProvidersProviderIdGET" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AchievementsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + head: + tags: + - "Achievements Provider" + description: "Get achivements provider with given id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "achievementsProvidersProviderIdHEAD" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + delete: + tags: + - "Achievements Provider" + description: "Remove an existing achiements provider with given id.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "achievementsProvidersProviderIdDELETE" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + patch: + tags: + - "Achievements Provider" + description: "Update an existing achivements provider with given id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "achievementsProvidersProviderIdPATCH" + parameters: + - name: "providerId" + in: "path" + description: "The provider id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AchievementsProvider" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AchievementsProvider" + /organizations: + get: + tags: + - "Organizations" + description: "Search organizations in the application. If no results, then\ + \ empty array is returned.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "organizationsGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "name" + in: "query" + description: "Filter by organization name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Organization" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + head: + tags: + - "Organizations" + description: "Retrieve header information for a search operation on organizations\ + \ in the application.\n\n**Security** - Note that for non-admin users, this\ + \ endpoint will only return entities that\nthe user has created.\n" + operationId: "organizationsHEAD" + parameters: + - name: "name" + in: "query" + description: "Filter by organization name" + required: false + type: "string" + responses: + "200": + description: "success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + post: + tags: + - "Organizations" + description: "Create a new Organization.\n\n**Security** - This endpoint is\ + \ accessible by all authenticated users. \n" + operationId: "organizationsPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Organization" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + /organizations/{organizationId}: + get: + tags: + - "Organizations" + description: "Get organization with given id.\n\n**Security** - Note that for\ + \ non-admin users, this endpoint will only return entities that\nthe user\ + \ has created. \n" + operationId: "organizationsOrganizationIdGET" + parameters: + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Organization" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + head: + tags: + - "Organizations" + description: "Get organization with given id, but only header information is\ + \ returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "organizationsOrganizationIdHEAD" + parameters: + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + delete: + tags: + - "Organizations" + description: "Remove an existing organization with given id.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "organizationsOrganizationIdDELETE" + parameters: + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + patch: + tags: + - "Organizations" + description: "Update an existing organization with given id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "organizationsOrganizationIdPATCH" + parameters: + - name: "organizationId" + in: "path" + description: "The organization id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/NameRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Organization" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Organizations" + /users/{userId}/attributes: + get: + tags: + - "User Attributes" + description: "Get attributes for the given user.\nOptionally, filter attributes\ + \ by the attribute name, attribute group name and attribute group id, given\ + \ an user id. If no results, then empty array is returned.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only return entities\ + \ that\nthe user has created.\n" + operationId: "usersUserIdAttributesGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeName" + in: "query" + description: "Filter by the attribute name" + required: false + type: "string" + - name: "attributeGroupName" + in: "query" + description: "Filter by the attribute group name" + required: false + type: "string" + - name: "attributeGroupId" + in: "query" + description: "Filter by the attribute group id" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/UserAttribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + head: + tags: + - "User Attributes" + description: "Retrieve header information for a search operation on user attributes\ + \ in the application.\n\n**Security** - Note that for non-admin users, this\ + \ endpoint will only return entities that\nthe user has created.\n" + operationId: "usersUserIdAttributesHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeName" + in: "query" + description: "Filter by the attribute name" + required: false + type: "string" + - name: "attributeGroupName" + in: "query" + description: "Filter by the attribute group name" + required: false + type: "string" + - name: "attributeGroupId" + in: "query" + description: "Filter by the attribute group id" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + post: + tags: + - "User Attributes" + description: "Create a new User Attribute.\n\n**Security** - This endpoint is\ + \ accessible by all authenticated users. \n" + operationId: "usersUserIdAttributesPOST" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserAttributeRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserAttribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + /users/{userId}/attributes/{attributeId}: + get: + tags: + - "User Attributes" + description: "Get User Attributes with given user and attribute id.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only return entities\ + \ that\nthe user has created. \n" + operationId: "usersUserIdAttributesAttributeIdGET" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserAttribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + head: + tags: + - "User Attributes" + description: "Get User Attributes with given ids, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created. \n" + operationId: "usersUserIdAttributesAttributeIdHEAD" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + delete: + tags: + - "User Attributes" + description: "Remove an existing User Attribute with given ids.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "usersUserIdAttributesAttributeIdDELETE" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + patch: + tags: + - "User Attributes" + description: "Update an existing user attribute with given ids.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "usersUserIdAttributesAttributeIdPATCH" + parameters: + - name: "userId" + in: "path" + description: "The user id" + required: true + type: "string" + format: "UUID" + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/UserAttributeUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/UserAttribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "UserAttributes" + /attributes: + get: + tags: + - "Attributes" + description: "Get list of attributes in the application. If no results, then\ + \ empty array is returned.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "attributesGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "attributeGroupId" + in: "query" + description: "Filter by attribute group id" + required: false + type: "string" + format: "UUID" + - name: "name" + in: "query" + description: "Filter by attribute name" + required: false + type: "string" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/Attribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + head: + tags: + - "Attributes" + description: "Retrieve header information for get operation on Attributes in\ + \ the application.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "attributesHEAD" + parameters: [] + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + post: + tags: + - "Attributes" + description: "Create a new Attribute.\n\n**Security** - This endpoint is accessible\ + \ by all authenticated users. \n" + operationId: "attributesPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AttributeRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Attribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + /attributes/{attributeId}: + get: + tags: + - "Attributes" + description: "Get Attribute by given attribute id.\n\n**Security** - Note that\ + \ for non-admin users, this endpoint will only return entities that\nthe user\ + \ has created. \n" + operationId: "attributesAttributeIdGET" + parameters: + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Attribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + head: + tags: + - "Attributes" + description: "Retrieve header information for get operation on Attribute by\ + \ its id in the application.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created. \ + \ \n" + operationId: "attributesAttributeIdHEAD" + parameters: + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + delete: + tags: + - "Attributes" + description: "Remove an existing attribute with given id.\n\n**Security** -\ + \ Note that this endpoint is only available for admin users. \n" + operationId: "attributesAttributeIdDELETE" + parameters: + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + patch: + tags: + - "Attributes" + description: "Update an existing attribute with given id.\n\n**Security** -\ + \ Note that for non-admin users, this endpoint will only allow updates on\ + \ entities that the\ncalling user has created.\n" + operationId: "attributesAttributeIdPATCH" + parameters: + - name: "attributeId" + in: "path" + description: "The attribute id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AttributeUpdateRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/Attribute" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "Attributes" + /attributeGroups: + get: + tags: + - "Attribute Groups" + description: "Search Attribute Groups in the application. Multiple filters\ + \ are supported.\nIf no results, then empty array is returned.\n\n**Security**\ + \ - Note that for non-admin users, this endpoint will only return entities\ + \ that\nthe user has created.\n" + operationId: "attributeGroupsGET" + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' + - name: "name" + in: "query" + description: "Filter by group name" + required: false + type: "string" + - name: "organizationId" + in: "query" + description: "Filter by organization id" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + type: "array" + items: + $ref: "#/definitions/AttributeGroup" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + head: + tags: + - "Attribute Groups" + description: "Retrieve header information for a search operation on Attribute\ + \ Groups in the application.\n\n**Security** - Note that for non-admin users,\ + \ this endpoint will only return entities that\nthe user has created.\n" + operationId: "attributeGroupsHEAD" + parameters: + - name: "name" + in: "query" + description: "Filter by group name" + required: false + type: "string" + - name: "organizationId" + in: "query" + description: "Filter by organization id" + required: false + type: "string" + format: "UUID" + responses: + "200": + description: "Success response" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + post: + tags: + - "Attribute Groups" + description: "Create a new Attribute Group.\n\n**Security** - This endpoint\ + \ is accessible by all authenticated users. \n" + operationId: "attributeGroupsPOST" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AttributeGroupRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AttributeGroup" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + /attributeGroups/{id}: + get: + tags: + - "Attribute Groups" + description: "Get Attribute Groups with given id.\n\n**Security** - Note that\ + \ for non-admin users, this endpoint will only return entities that\nthe user\ + \ has created. \n" + operationId: "attributeGroupsIdGET" + parameters: + - name: "id" + in: "path" + description: "The id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AttributeGroup" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + head: + tags: + - "Attribute Groups" + description: "Get Attribute Group with given id, but only header information\ + \ is returned.\n\n**Security** - Note that for non-admin users, this endpoint\ + \ will only return entities that\nthe user has created.\n" + operationId: "attributeGroupsIdHEAD" + parameters: + - name: "id" + in: "path" + description: "The id" + required: true + type: "string" + format: "UUID" + responses: + "200": + description: "OK - the request was successful" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + delete: + tags: + - "Attribute Groups" + description: "Remove an existing Attribute Group with given id.\n\n**Security**\ + \ - Note that this endpoint is only available for admin users. \n" + operationId: "attributeGroupsIdDELETE" + parameters: + - name: "id" + in: "path" + description: "The id" + required: true + type: "string" + format: "UUID" + responses: + "204": + description: "OK - the request was successful" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" + patch: + tags: + - "Attribute Groups" + description: "Update an existing Attribute Group with given id. Only the fields\ + \ in the request body are updated.\n\n**Security** - Note that for non-admin\ + \ users, this endpoint will only allow updates on entities that the\ncalling\ + \ user has created.\n" + operationId: "attributeGroupsIdPATCH" + parameters: + - name: "id" + in: "path" + description: "The id" + required: true + type: "string" + format: "UUID" + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/AttributeGroupRequestBody" + responses: + "200": + description: "OK - the request was successful" + schema: + $ref: "#/definitions/AttributeGroup" + "400": + $ref: "#/definitions/BadRequest" + "401": + $ref: "#/definitions/Unauthorized" + "403": + $ref: "#/definitions/Forbidden" + "404": + $ref: "#/definitions/NotFound" + "409": + $ref: "#/definitions/Conflict" + "500": + $ref: "#/definitions/ServerError" + security: + - Bearer: [] + x-swagger-router-controller: "AttributeGroups" +securityDefinitions: + Bearer: + type: "apiKey" + name: "Authorization" + in: "header" + +parameters: + page: + name: page + in: query + description: The page number. + required: false + type: integer + default: 1 + perPage: + name: perPage + in: query + description: The number of items to list per page. + required: false + type: integer + default: 20 + maximum: 100 + +definitions: + AuditFields: + type: "object" + required: + - "created" + - "createdBy" + - "updated" + - "updatedBy" + properties: + created: + type: "string" + format: "date-time" + description: "When the entity was created." + updated: + type: "string" + format: "date-time" + description: "When the entity was updated." + createdBy: + type: "string" + format: "UUID" + description: "Creator of the entity." + updatedBy: + type: "string" + format: "UUID" + description: "User that last updated the entity." + description: "Describes the audit fields that are present in all the models in\ + \ this API." + Achievement: + allOf: + - type: "object" + required: + - "achievementsProviderId" + - "certifierId" + - "name" + - "uri" + - "userId" + properties: + userId: + type: "string" + format: "UUID" + description: "The id of user that this Achievement belongs to." + achievementsProviderId: + type: "string" + format: "UUID" + description: "The id of achievements provider for this Achievement." + name: + type: "string" + description: "Name of achievement." + uri: + type: "string" + description: "Uri of achievement" + certifierId: + type: "string" + description: "Id of certifier" + certifiedDate: + type: "string" + format: "date-time" + description: "The date when certification occurred." + - $ref: "#/definitions/AuditFields" + AchievementRequestBody: + allOf: + - type: "object" + properties: + achievementsProviderId: + type: "string" + format: "UUID" + description: "The id of provider for this Achievement." + - $ref: "#/definitions/AchievementUpdateRequestBody" + AchievementUpdateRequestBody: + type: "object" + properties: + name: + type: "string" + description: "Name of Achievement" + uri: + type: "string" + description: "Uri of Achievement" + certifierId: + type: "string" + description: "Id of certifier" + certifiedDate: + type: "string" + format: "date-time" + description: "The date when certification occurred." + example: + certifierId: "certifierId" + certifiedDate: "2000-01-23T04:56:07.000+00:00" + name: "name" + uri: "uri" + AchievementsProvider: + allOf: + - type: "object" + required: + - "id" + - "name" + properties: + id: + type: "string" + format: "UUID" + description: "The id of the provider." + name: + type: "string" + description: "The name of the provider." + - $ref: "#/definitions/AuditFields" + Skill: + allOf: + - type: "object" + required: + - "externalId" + - "id" + - "name" + - "skillProviderId" + - "uri" + properties: + id: + type: "string" + format: "UUID" + description: "The skill id" + skillProviderId: + type: "string" + format: "UUID" + description: "The referenced skill provider id" + name: + type: "string" + description: "The name of the skill" + externalId: + type: "string" + description: "The external id for the skill" + uri: + type: "string" + description: "The uri for the skill" + - $ref: "#/definitions/AuditFields" + SkillRequestBody: + allOf: + - type: "object" + - $ref: "#/definitions/SkillUpdateRequestBody" + SkillUpdateRequestBody: + type: "object" + properties: + skillProviderId: + type: "string" + format: "UUID" + description: "The id of provider for this Skill." + name: + type: "string" + description: "Name of Skill" + uri: + type: "string" + description: "Uri of Skill" + externalId: + type: "string" + description: "External Id of skill" + example: + skillProviderId: "skillProviderId" + name: "name" + externalId: "externalId" + uri: "uri" + SkillsProvider: + allOf: + - type: "object" + required: + - "id" + - "name" + properties: + id: + type: "string" + format: "UUID" + description: "The id of the provider." + name: + type: "string" + description: "The name of the provider." + - $ref: "#/definitions/AuditFields" + ExternalProfile: + allOf: + - type: "object" + required: + - "organizationId" + - "uri" + - "userId" + properties: + userId: + type: "string" + format: "UUID" + description: "The id of the user this profile belongs to." + organizationId: + type: "string" + format: "UUID" + description: "The id of the organization this profile belongs to." + uri: + type: "string" + description: "The uri of the external profile." + - $ref: "#/definitions/AuditFields" + ExternalProfileRequestBody: + type: "object" + required: + - "organizationId" + - "uri" + properties: + organizationId: + type: "string" + format: "UUID" + description: "The id of the organization this profile belongs to." + uri: + type: "string" + description: "The uri of the external profile." + example: + organizationId: "organizationId" + uri: "uri" + ExternalProfileUpdateRequestBody: + type: "object" + properties: + uri: + type: "string" + description: "The uri of the external profile." + example: + uri: "uri" + Role: + allOf: + - type: "object" + required: + - "id" + - "name" + properties: + id: + type: "string" + format: "UUID" + description: "The id of the role." + name: + type: "string" + description: "The name of the role." + - $ref: "#/definitions/AuditFields" + UserRole: + allOf: + - type: "object" + required: + - "userId" + properties: + userId: + type: "string" + format: "UUID" + description: "The user to be associated with the role" + - $ref: "#/definitions/UserRoleRequestBody" + - $ref: "#/definitions/AuditFields" + UserRoleRequestBody: + type: "object" + required: + - "roleId" + properties: + roleId: + type: "string" + format: "UUID" + description: "The roleId of this user role." + description: "Represents a Role that belongs to a given user.\n" + example: + roleId: "roleId" + User: + allOf: + - type: "object" + required: + - "handle" + - "firstName" + - "lastName" + - "id" + properties: + id: + type: "string" + format: "UUID" + description: "The id of the user." + handle: + type: "string" + description: "The handle of the user." + firstName: + type: "string" + description: "The first name of the user." + lastName: + type: "string" + description: "The last name of the user." + - $ref: "#/definitions/AuditFields" + UserSkill: + allOf: + - type: "object" + required: + - "certifiedDate" + - "certifierId" + - "metricValue" + - "skillId" + - "userId" + properties: + userId: + type: "string" + format: "UUID" + description: "The id of user that this Skill belongs to." + skillId: + type: "string" + format: "UUID" + description: "The Skill id." + metricValue: + type: "string" + description: "The skill metric value." + certifierId: + type: "string" + description: "Id of certifier" + certifiedDate: + type: "string" + format: "date-time" + description: "The date when certification occurred." + - $ref: "#/definitions/AuditFields" + UserSkillRequestBody: + allOf: + - type: "object" + properties: + skillId: + type: "string" + format: "UUID" + description: "The Skill id." + example: + certifierId: "certifierId" + certifiedDate: "2000-01-23T04:56:07.000+00:00" + metricValue: "metricValue" + skillId: "string" + - $ref: "#/definitions/UserSkillUpdateRequestBody" + UserSkillUpdateRequestBody: + type: "object" + properties: + metricValue: + type: "string" + description: "The skill metric value." + certifierId: + type: "string" + description: "Id of certifier" + certifiedDate: + type: "string" + format: "date-time" + description: "The date when certification occurred." + example: + certifierId: "certifierId" + certifiedDate: "2000-01-23T04:56:07.000+00:00" + metricValue: "metricValue" + UserAttribute: + allOf: + - type: "object" + required: + - "attributeId" + - "userId" + - "value" + properties: + userId: + type: "string" + format: "UUID" + description: "The id of user that this user attribute belongs to." + attributeId: + type: "string" + format: "UUID" + description: "The attribute id." + value: + type: "string" + description: "The user attribute value." + - $ref: "#/definitions/AuditFields" + UserAttributeRequestBody: + allOf: + - type: "object" + properties: + attributeId: + type: "string" + format: "UUID" + description: "The attribute id." + - $ref: "#/definitions/UserAttributeUpdateRequestBody" + UserAttributeUpdateRequestBody: + type: "object" + properties: + value: + type: "string" + description: "The user attribute value." + example: + value: "value" + UserRequestBody: + type: "object" + properties: + handle: + type: "string" + description: "The handle of the user." + firstName: + type: "string" + description: "The first name of the user." + lastName: + type: "string" + description: "The last name of the user." + description: "Properties that are provided when creating or editing a User.\n" + example: + handle: "handle" + firstName: "John" + lastName: "Doe" + UserUpdateRequestBody: + type: "object" + properties: + handle: + type: "string" + description: "The handle of the user." + firstName: + type: "string" + description: "The first name of the user." + lastName: + type: "string" + description: "The last name of the user." + example: + handle: "handle" + firstName: "John" + lastName: "Doe" + AttributeGroup: + allOf: + - type: "object" + required: + - "id" + - "name" + - "organizationId" + properties: + id: + type: "string" + format: "UUID" + description: "Id of the AttributeGroup" + name: + type: "string" + description: "Name of the AttributeGroup" + organizationId: + type: "string" + format: "UUID" + description: "Id of the organization that this attribute group belongs to." + - $ref: "#/definitions/AuditFields" + AttributeGroupRequestBody: + type: "object" + required: + - "name" + - "organizationId" + properties: + name: + type: "string" + description: "Name of the entity" + organizationId: + type: "string" + format: "UUID" + description: "Id of the organization that this attribute group belongs to." + description: "Request body containing the fields for an Attribute Group." + example: + organizationId: "organizationId" + name: "name" + Organization: + allOf: + - type: "object" + required: + - "id" + - "name" + properties: + id: + type: "string" + format: "UUID" + description: "Id of the organization" + name: + type: "string" + description: "Name of the organization" + - $ref: "#/definitions/AuditFields" + NameRequestBody: + type: "object" + required: + - "name" + properties: + name: + type: "string" + description: "Name of the entity" + description: "Simple request body containing the name of the entity." + example: + name: "name" + Attribute: + allOf: + - type: "object" + required: + - "attributeGroupId" + - "id" + - "name" + properties: + id: + type: "string" + format: "UUID" + description: "The attribute id" + attributeGroupId: + type: "string" + format: "UUID" + description: "The referenced attribute group id" + name: + type: "string" + description: "The name of the attribute" + - $ref: "#/definitions/AuditFields" + AttributeRequestBody: + allOf: + - $ref: "#/definitions/AttributeUpdateRequestBody" + - {} + AttributeUpdateRequestBody: + type: "object" + properties: + attributeGroupId: + type: "string" + format: "UUID" + description: "The id of provider for this attribute." + name: + type: "string" + description: "Name of attribute" + example: + name: "name" + attributeGroupId: "attributeGroupId" + Unauthorized: + type: "object" + properties: + message: + type: "string" + example: "Unable to authenticate the user." + description: "The unauthorized error message." + description: "The unauthorized error entity." + NotFound: + type: "object" + properties: + message: + type: "string" + example: "A resource with the name could not be found." + description: "The not found error message." + description: "The not found error entity." + ServerError: + type: "object" + properties: + message: + type: "string" + example: "Something went wrong while processing your request. We're sorry\ + \ for the trouble. We've been notified of the error and will correct it\ + \ as soon as possible. Please try your request again in a moment." + description: "The server error message." + description: "The server error entity." + BadRequest: + type: "object" + properties: + message: + type: "string" + example: "Invalid input." + description: "The bad request error message." + description: "The bad request error entity." + Forbidden: + type: "object" + properties: + message: + type: "string" + example: "You are not allowed to access the request." + description: "The forbidden error message." + description: "The permission error entity." + Conflict: + type: "object" + required: + - "message" + properties: + message: + type: "string" + example: "Creating a resource with a name already exists." + description: "The conflict error message." + description: "The conflict error entity." diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5a98387 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3667 @@ +{ + "name": "ubahn-api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/highlight": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz", + "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.0", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==" + }, + "@hapi/formula": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-1.2.0.tgz", + "integrity": "sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==" + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" + }, + "@hapi/joi": { + "version": "16.1.8", + "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-16.1.8.tgz", + "integrity": "sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==", + "requires": { + "@hapi/address": "^2.1.2", + "@hapi/formula": "^1.2.0", + "@hapi/hoek": "^8.2.4", + "@hapi/pinpoint": "^1.0.2", + "@hapi/topo": "^3.1.3" + } + }, + "@hapi/joi-date": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/joi-date/-/joi-date-2.0.1.tgz", + "integrity": "sha512-8be8JUEC8Wm1Do3ryJy+TXmkAL13b2JwXn7gILBoor8LopY/M+hJskodzOOxfJdckkfWnbmbnMEyJW3d/gZMfA==", + "requires": { + "moment": "2.x.x" + } + }, + "@hapi/pinpoint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-1.0.2.tgz", + "integrity": "sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==" + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz", + "integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-jwt": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", + "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", + "requires": { + "@types/express": "*", + "@types/express-unless": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.8", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz", + "integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/express-unless": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz", + "integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==", + "requires": { + "@types/express": "*" + } + }, + "@types/mime": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", + "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==" + }, + "@types/node": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz", + "integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==" + }, + "@types/qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@types/serve-static": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", + "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "amazon-qldb-driver-nodejs": { + "version": "0.1.1-preview.2", + "resolved": "https://registry.npmjs.org/amazon-qldb-driver-nodejs/-/amazon-qldb-driver-nodejs-0.1.1-preview.2.tgz", + "integrity": "sha512-ia1benMLRS25KWDbg3d6MhX48+2rTUZzQ3LXM4mxu0/1V9ooUFKfZeyCmueZ1zrqn572Z8uO8DPq/1T1TZsI4Q==", + "requires": { + "@types/node": "12.0.2", + "ion-hash-js": "^1.0.2", + "semaphore-async-await": "^1.5.1" + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sdk": { + "version": "2.668.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.668.0.tgz", + "integrity": "sha512-mmZJmeenNM9hRR4k+JAStBhYFym2+VCPTRWv0Vn2oqqXIaIaNVdNf9xag/WMG8b8M80R3XXfVHKmDPST0/EfHA==", + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" + }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, + "babel-runtime": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.6.1.tgz", + "integrity": "sha1-eIuUtvY04luRvWxd9y1GdFevsAA=", + "requires": { + "core-js": "^2.1.0" + } + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, + "bunyan": { + "version": "1.8.14", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz", + "integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==", + "requires": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "codependency": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/codependency/-/codependency-0.1.4.tgz", + "integrity": "sha1-0XY6tyZL1wyR2WJumIYtN5K/jUo=", + "requires": { + "semver": "5.0.1" + }, + "dependencies": { + "semver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", + "integrity": "sha1-n7P0AE+QDYPEeWj+QvdYPgWDLMk=" + } + } + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "config": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz", + "integrity": "sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==", + "requires": { + "json5": "^2.1.1" + } + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "deglob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-4.0.1.tgz", + "integrity": "sha512-/g+RDZ7yf2HvoW+E5Cy+K94YhgcFgr6C8LuHZD1O5HoNPkf3KY6RfXJ0DBGlB/NkLi5gml+G9zqRzk9S0mHZCg==", + "dev": true, + "requires": { + "find-root": "^1.0.0", + "glob": "^7.0.5", + "ignore": "^5.0.0", + "pkg-config": "^1.1.0", + "run-parallel": "^1.1.2", + "uniq": "^1.0.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "optional": true, + "requires": { + "nan": "^2.14.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "elasticsearch": { + "version": "16.7.1", + "resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.1.tgz", + "integrity": "sha512-PL/BxB03VGbbghJwISYvVcrR9KbSSkuQ7OM//jHJg/End/uC2fvXg4QI7RXLvCGbhBuNQ8dPue7DOOPra73PCw==", + "requires": { + "agentkeepalive": "^3.4.1", + "chalk": "^1.0.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.x" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "env-variable": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", + "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz", + "integrity": "sha512-EF6XkrrGVbvv8hL/kYa/m6vnvmUT+K82pJJc4JJVMM6+Qgqh0pnwprSxdduDLB9p/7bIxD+YV5O0wfb8lmcPbA==", + "dev": true + }, + "eslint-config-standard-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-8.1.0.tgz", + "integrity": "sha512-ULVC8qH8qCqbU792ZOO6DaiaZyHNS/5CZt3hKqHkEhVlhPEPN3nfBqqxJCyp59XrjIBZPu1chMYe9T2DXZ7TMw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-2.0.0.tgz", + "integrity": "sha512-f6fceVtg27BR02EYnBhgWLFQfK6bN4Ll0nQFrBHOlCsAyxeZkn0NHns5O0YZOPrV1B3ramd6cgFwaoFLcSkwEQ==", + "dev": true, + "requires": { + "eslint-utils": "^1.4.2", + "regexpp": "^3.0.0" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-10.0.0.tgz", + "integrity": "sha512-1CSyM/QCjs6PXaT18+zuAXsjXGIGo5Rw630rSKwokSs2jrYURQc4R5JZpoanNCqwNmepg+0eZ9L7YiRUJb8jiQ==", + "dev": true, + "requires": { + "eslint-plugin-es": "^2.0.0", + "eslint-utils": "^1.4.2", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-promise": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", + "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "dev": true + }, + "eslint-plugin-react": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz", + "integrity": "sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "doctrine": "^2.1.0", + "has": "^1.0.3", + "jsx-ast-utils": "^2.1.0", + "object.entries": "^1.1.0", + "object.fromentries": "^2.0.0", + "object.values": "^1.1.0", + "prop-types": "^15.7.2", + "resolve": "^1.10.1" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-plugin-standard": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", + "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "dev": true + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-parameter-names": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/get-parameter-names/-/get-parameter-names-0.3.0.tgz", + "integrity": "sha1-LSI3zVkubFuFmrLv2rQ18Ajlu5c=" + }, + "get-stdin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", + "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "hoek": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-5.0.4.tgz", + "integrity": "sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w==" + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "http-aws-es": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/http-aws-es/-/http-aws-es-6.0.0.tgz", + "integrity": "sha512-g+qp7J110/m4aHrR3iit4akAlnW0UljZ6oTq/rCcbsI8KP9x+95vqUtx49M2XQ2JMpwJio3B6gDYx+E8WDxqiA==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ion-hash-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ion-hash-js/-/ion-hash-js-1.0.3.tgz", + "integrity": "sha512-c1U7DW9icOrof1cfzktq/8zaPXLneDJqZhYydhTmNBbHJ+D6JHTFydp5fu+PlwirgEt6qB+wPUcaVdJPQgqKug==", + "requires": { + "ion-js": "^3.1.2" + } + }, + "ion-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/ion-js/-/ion-js-3.1.2.tgz", + "integrity": "sha512-pujfIqzKMEfxBBD98vcJQcPVlckM0f3ETd+x6+SPYq50xD4li2sCZ9ODCPAGlT8stRqHa8aGuJ1puwrAoyTcxw==", + "requires": { + "jsbi": "^3.1.1" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-callable": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "dev": true + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "requires": { + "punycode": "2.x.x" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "joi": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-13.7.0.tgz", + "integrity": "sha512-xuY5VkHfeOYK3Hdi91ulocfuFopwgbSORmIwzcwHKESQhC7w1kD5jaVSPnqDxS2I8t3RZ9omCKAxNwXN5zG1/Q==", + "requires": { + "hoek": "5.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbi": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.2.tgz", + "integrity": "sha512-5nDXo1X9QVaXK/Cpb5VECV9ss1QPbjUuk1qSruHB1PK/g39Sd414K4nci99ElFDZv0vzxDEnKn3o49/Tn9Yagw==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jsx-ast-utils": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", + "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jwks-rsa": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.8.1.tgz", + "integrity": "sha512-CcE8ypsATHwGmzELwzeFjLzPBXTXTrMmDYbn92LTQwYsZdOedp+ZIuYTofUdrWreu8CKRuXmhk17+6/li2sR6g==", + "requires": { + "@types/express-jwt": "0.0.42", + "axios": "^0.19.2", + "debug": "^4.1.0", + "jsonwebtoken": "^8.5.1", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2", + "ms": "^2.1.2" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "requires": { + "colornames": "^1.1.1" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "lru-memoizer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.2.tgz", + "integrity": "sha512-N5L5xlnVcbIinNn/TJ17vHBZwBMt9t7aJDz2n97moWubjNl6VO9Ao2XuAGBBddkYdjrwR9HfzXbT6NfMZXAZ/A==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "millisecond": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/millisecond/-/millisecond-0.1.2.tgz", + "integrity": "sha1-bMWtOGJByrjniv+WT4cCjuyS2sU=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.1.tgz", + "integrity": "sha512-nRKMf9wDS4Fkyd0C9LXh2FFXinD+iwbJ5p/lh3CHitW9kZbRbJ8hCruiadiIXZVbeAqKZzqcTvHnK3mRhFjb6w==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + } + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "optional": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-cache": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz", + "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==", + "requires": { + "clone": "2.x" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz", + "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "dev": true, + "requires": { + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "dev": true, + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "r7insight_node": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/r7insight_node/-/r7insight_node-1.8.4.tgz", + "integrity": "sha512-6cQrzLkaOxdv/SRFXWRJjgFr8a3nXUOT/4IMFuBv+mWzBnu5DJl+HzONAsWYvclrlZnvfa54PaIPqPuPRSlbrQ==", + "requires": { + "babel-runtime": "6.6.1", + "codependency": "0.1.4", + "json-stringify-safe": "5.0.1", + "lodash": "4.17.15", + "reconnect-core": "1.3.0", + "semver": "5.1.0" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "semver": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", + "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" + } + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "reconnect-core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", + "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", + "requires": { + "backoff": "~2.5.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "semaphore-async-await": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz", + "integrity": "sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo=" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "standard": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/standard/-/standard-14.3.3.tgz", + "integrity": "sha512-HBEAD5eVXrr2o/KZ3kU8Wwaxw90wzoq4dOQe6vlRnPoQ6stn4LCLRLBBDp0CjH/aOTL9bDZJbRUOZcBaBnNJ0A==", + "dev": true, + "requires": { + "eslint": "~6.8.0", + "eslint-config-standard": "14.1.0", + "eslint-config-standard-jsx": "8.1.0", + "eslint-plugin-import": "~2.18.0", + "eslint-plugin-node": "~10.0.0", + "eslint-plugin-promise": "~4.2.1", + "eslint-plugin-react": "~7.14.2", + "eslint-plugin-standard": "~4.0.0", + "standard-engine": "^12.0.0" + } + }, + "standard-engine": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-12.0.0.tgz", + "integrity": "sha512-gJIIRb0LpL7AHyGbN9+hJ4UJns37lxmNTnMGRLC8CFrzQ+oB/K60IQjKNgPBCB2VP60Ypm6f8DFXvhVWdBOO+g==", + "dev": true, + "requires": { + "deglob": "^4.0.0", + "get-stdin": "^7.0.0", + "minimist": "^1.1.0", + "pkg-conf": "^3.1.0" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "swagger-ui-dist": { + "version": "3.25.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.25.3.tgz", + "integrity": "sha512-/8DSx431mdN94t8mIZejhVUdN9r8zM+V1l+VGT0h7smrzYFa9vWi2sLVCg4YfgKgMjXYhU4OKADHPnWkbLb+ZQ==" + }, + "swagger-ui-express": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.4.tgz", + "integrity": "sha512-Ea96ecpC+Iq9GUqkeD/LFR32xSs8gYqmTW1gXCuKg81c26WV6ZC2FsBSPVExQP6WkyUuz5HEiR0sEv/HCC343g==", + "requires": { + "swagger-ui-dist": "^3.18.1" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "tc-bus-api-wrapper": { + "version": "github:topcoder-platform/tc-bus-api-wrapper#f8cbd335a0e0b4d6edd7cae859473593271fd97f", + "from": "github:topcoder-platform/tc-bus-api-wrapper", + "requires": { + "joi": "^13.4.0", + "lodash": "^4.17.15", + "superagent": "^3.8.3", + "tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6.4" + }, + "dependencies": { + "tc-core-library-js": { + "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", + "from": "github:appirio-tech/tc-core-library-js#v2.6.4", + "requires": { + "axios": "^0.19.0", + "bunyan": "^1.8.12", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.6.0", + "lodash": "^4.17.15", + "millisecond": "^0.1.2", + "r7insight_node": "^1.8.4", + "request": "^2.88.0" + } + } + } + }, + "tc-core-library-js": { + "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", + "from": "github:appirio-tech/tc-core-library-js#v2.6.4", + "requires": { + "axios": "^0.19.0", + "bunyan": "^1.8.12", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.6.0", + "lodash": "^4.17.15", + "millisecond": "^0.1.2", + "r7insight_node": "^1.8.4", + "request": "^2.88.0" + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "topo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", + "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", + "requires": { + "hoek": "6.x.x" + }, + "dependencies": { + "hoek": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" + } + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "winston": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3defd2e --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "ubahn-api", + "version": "1.0.0", + "description": "UBahn api", + "main": "app.js", + "scripts": { + "start": "node app.js", + "lint": "standard \"**/*.js\"", + "insert-data": "node scripts/db/genData.js", + "delete-data": "node scripts/db/dropAll.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "dependencies": { + "@hapi/joi": "^16.1.8", + "@hapi/joi-date": "^2.0.1", + "amazon-qldb-driver-nodejs": "^0.1.1-preview.2", + "aws-sdk": "^2.627.0", + "axios": "^0.19.2", + "body-parser": "^1.19.0", + "config": "^3.2.4", + "cors": "^2.8.5", + "elasticsearch": "^16.7.1", + "express": "^4.17.1", + "get-parameter-names": "^0.3.0", + "http-aws-es": "^6.0.0", + "ion-js": "^3.1.2", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "node-cache": "^5.1.2", + "querystring": "^0.2.0", + "swagger-ui-express": "^4.1.4", + "tc-bus-api-wrapper": "github:topcoder-platform/tc-bus-api-wrapper", + "tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6.4", + "uuid": "^7.0.1", + "winston": "^3.2.1" + }, + "devDependencies": { + "standard": "^14.3.0" + }, + "engines": { + "node": "12.x" + } +} diff --git a/scripts/constants.js b/scripts/constants.js new file mode 100644 index 0000000..755b650 --- /dev/null +++ b/scripts/constants.js @@ -0,0 +1,96 @@ +/** + * This module contains es resources configuration. + * Identical to the one from ES processor, but updated to work with the api + */ + +const config = require('config') + +const topResources = { + achievementprovider: { + index: config.get('ES.DOCUMENTS.achievementprovider.index'), + type: config.get('ES.DOCUMENTS.achievementprovider.type') + }, + attribute: { + index: config.get('ES.DOCUMENTS.attribute.index'), + type: config.get('ES.DOCUMENTS.attribute.type') + }, + attributegroup: { + index: config.get('ES.DOCUMENTS.attributegroup.index'), + type: config.get('ES.DOCUMENTS.attributegroup.type') + }, + organization: { + index: config.get('ES.DOCUMENTS.organization.index'), + type: config.get('ES.DOCUMENTS.organization.type') + }, + role: { + index: config.get('ES.DOCUMENTS.role.index'), + type: config.get('ES.DOCUMENTS.role.type') + }, + skill: { + index: config.get('ES.DOCUMENTS.skill.index'), + type: config.get('ES.DOCUMENTS.skill.type') + }, + skillprovider: { + index: config.get('ES.DOCUMENTS.skillprovider.index'), + type: config.get('ES.DOCUMENTS.skillprovider.type') + }, + user: { + index: config.get('ES.DOCUMENTS.user.index'), + type: config.get('ES.DOCUMENTS.user.type') + } +} + +const userResources = { + achievement: { + propertyName: config.get('ES.DOCUMENTS.achievement.userField'), + relateKey: 'achievementsProviderId' + }, + externalprofile: { + propertyName: config.get('ES.DOCUMENTS.externalprofile.userField'), + relateKey: 'organizationId' + }, + userattribute: { + propertyName: config.get('ES.DOCUMENTS.userattribute.userField'), + relateKey: 'attributeId', + nested: true + }, + userrole: { + propertyName: config.get('ES.DOCUMENTS.userrole.userField'), + relateKey: 'roleId' + }, + userskill: { + propertyName: config.get('ES.DOCUMENTS.userskill.userField'), + relateKey: 'skillId' + } +} + +const organizationResources = { + organizationskillprovider: { + propertyName: config.get('ES.DOCUMENTS.organizationskillprovider.orgField'), + relateKey: 'skillProviderId' + } +} + +const modelToESIndexMapping = { + User: 'user', + Role: 'role', + SkillsProvider: 'skillprovider', + Organization: 'organization', + Skill: 'skill', + UsersRole: 'userrole', + UsersSkill: 'userskill', + Achievement: 'achievement', + ExternalProfile: 'externalprofile', + AchievementsProvider: 'achievementprovider', + AttributeGroup: 'attributegroup', + Attribute: 'attribute', + UserAttribute: 'userattribute', + OrganizationSkillsProvider: 'organizationskillprovider' +} + +module.exports = { + topResources, + userResources, + organizationResources, + modelToESIndexMapping +} diff --git a/scripts/db/data/Achievement.json b/scripts/db/data/Achievement.json new file mode 100644 index 0000000..d7f37e5 --- /dev/null +++ b/scripts/db/data/Achievement.json @@ -0,0 +1,41 @@ +[ + { + "id": "ae6fcff4-c969-42df-ba1c-25dbfb0651e8", + "created": "2020-05-13T06:43:20.003Z", + "updated": "2020-05-13T06:48:04.687Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "achievementsProviderId": "eb327c00-0090-45af-96d2-593408c96397", + "name": "Informatika", + "uri": "string", + "certifierId": "string", + "certifiedDate": "2020-05-13T06:33:54.708Z", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "416c37b2-ddd8-4a12-9fac-b4ac83d00c88", + "created": "2020-05-13T08:44:27.244Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "achievementsProviderId": "ce05133f-129e-484d-9ef9-72bf51ff81f9", + "name": "Upwork", + "uri": "http://www.google.com/xx", + "certifierId": "certifierId", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "a49e1013-fd42-4c08-bc12-492510cadb96", + "created": "2020-05-13T08:44:27.244Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "achievementsProviderId": "ce05133f-129e-484d-9ef9-72bf51ff81f9", + "name": "Topcoder", + "uri": "http://www.google.com/xx", + "certifierId": "certifierId", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + } +] diff --git a/scripts/db/data/AchievementsProvider.json b/scripts/db/data/AchievementsProvider.json new file mode 100644 index 0000000..e6e1943 --- /dev/null +++ b/scripts/db/data/AchievementsProvider.json @@ -0,0 +1,26 @@ +[ + { + "id": "edd852be-70c8-45d6-9c38-63188d868d67", + "created": "2020-05-13T06:23:54.712Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "name": "achievementsProviders_01" + }, + { + "id": "ce05133f-129e-484d-9ef9-72bf51ff81f9", + "created": "2020-05-13T08:42:41.877Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "name": "achievementsProviders_02" + }, + { + "id": "eb327c00-0090-45af-96d2-593408c96397", + "created": "2020-05-13T06:25:17.458Z", + "updated": "2020-05-13T06:26:56.374Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "name": "achievementsProviders_update" + } +] \ No newline at end of file diff --git a/scripts/db/data/Attribute.json b/scripts/db/data/Attribute.json new file mode 100644 index 0000000..14b41b8 --- /dev/null +++ b/scripts/db/data/Attribute.json @@ -0,0 +1,74 @@ +[ + { + "id": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "created": "2020-05-13T08:46:39.030Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "isAvailable" + }, + { + "id": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "created": "2020-05-13T08:35:45.512Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "company" + }, + { + "id": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "location" + }, + { + "id": "c44d4bee-1356-46d6-9f1f-991936dec297", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "title" + }, + { + "id": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "Billing Account" + }, + { + "id": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "Gender" + }, + { + "id": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "Experience (in months)" + }, + { + "id": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "created": "2020-05-13T07:32:03.128Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "name": "On internship" + } +] diff --git a/scripts/db/data/AttributeGroup.json b/scripts/db/data/AttributeGroup.json new file mode 100644 index 0000000..aeaf9e5 --- /dev/null +++ b/scripts/db/data/AttributeGroup.json @@ -0,0 +1,20 @@ +[ + { + "id": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", + "created": "2020-05-13T08:45:33.291Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "organizationId": "854f1bf3-6f51-424b-866f-e5f5a5803904", + "name": "attributeGroup_03" + }, + { + "id": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "created": "2020-05-13T07:15:01.215Z", + "updated": "2020-05-13T07:16:20.636Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "name": "group 03" + } +] \ No newline at end of file diff --git a/scripts/db/data/ExternalProfile.json b/scripts/db/data/ExternalProfile.json new file mode 100644 index 0000000..0cd4563 --- /dev/null +++ b/scripts/db/data/ExternalProfile.json @@ -0,0 +1,350 @@ +[ + { + "id": "9febace8-b5c7-4fdc-b966-ecb27bca3337", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1234567", + "uri": "http://www.new.com/new-uri" + }, + { + "id": "f2d1b567-8ea3-4eec-93b0-32378a19edb7", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8547899", + "uri": "http://www.new.com/new-uri" + }, + { + "id": "f2d1b567-8ea3-4eec-93b0-32378a19edb7", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f", + "organizationId": "6a21394e-1278-4835-9e4d-cb4ff151fcd3", + "externalId": "23124329", + "uri": "http://www.new.com/new-uri" + }, + { + "id": "f2d1b567-8ea3-4eec-93b0-32378a19edb7", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "23225544", + "uri": "http://www.new.com/new-uri" + }, + { + "id": "8c695f11-a7ec-46dc-8a3c-b6ffe645eac5", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "2838084", + "uri": "https://alfonzo.name" + }, + { + "id": "50a46957-b5aa-4a4c-845e-00d6689c24de", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8637137", + "uri": "https://ora.com" + }, + { + "id": "74d83b36-3dd4-4b03-b76c-99fe9164e2ef", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "7724780", + "uri": "http://alanna.org" + }, + { + "id": "06a94918-64ff-463c-b50b-edd7d4207365", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "3180776", + "uri": "http://clare.net" + }, + { + "id": "e6c4f112-3e0d-4301-ad37-ca0228984de2", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8944692", + "uri": "http://blake.org" + }, + { + "id": "7791e232-edf1-480f-a9fe-29817ddff22c", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8450763", + "uri": "https://adolph.biz" + }, + { + "id": "dca21f52-9853-456c-898c-49de7122f70a", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8712185", + "uri": "http://donna.name" + }, + { + "id": "0700e32a-b92b-4918-ad8f-8dceb7364280", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "5876965", + "uri": "https://terry.info" + }, + { + "id": "e7d9d2be-d334-4042-a130-fbb49c92679c", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "2154615", + "uri": "https://kavon.biz" + }, + { + "id": "dda4b112-9b44-406f-b6cc-60ce066f67af", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8591690", + "uri": "https://stefan.info" + }, + { + "id": "49a69a61-ed83-4bd1-a760-a3b0e7931e32", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1262746", + "uri": "http://elaina.info" + }, + { + "id": "7bd0d5cd-b0b4-4437-a9d9-99b6a9b55e28", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "5638359", + "uri": "https://jovanny.biz" + }, + { + "id": "9454adfb-cd8d-4eed-9632-6e25fec6947f", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "2390800", + "uri": "http://sandrine.com" + }, + { + "id": "75cda255-22f8-4732-bf82-5b9a609be036", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1588616", + "uri": "http://bernice.biz" + }, + { + "id": "966f8064-4546-4477-8b81-be6b1ec9ea65", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8855875", + "uri": "http://leonor.name" + }, + { + "id": "36a4dc68-27ab-4856-a830-d04938a176e4", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "3847475", + "uri": "http://jillian.name" + }, + { + "id": "88b48539-50f3-4ce1-818d-bc4e26555487", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1546137", + "uri": "https://aileen.com" + }, + { + "id": "d9209e89-01b0-4436-b2a4-0a1230b346a4", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "7772959", + "uri": "http://lincoln.biz" + }, + { + "id": "997b2713-52d5-4e8f-b312-e7d5a3f6f7af", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "7076592", + "uri": "https://lourdes.name" + }, + { + "id": "52a7a9cc-e7ac-4894-93a2-16bb54c8159a", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "9358202", + "uri": "https://anabelle.name" + }, + { + "id": "2af2449b-db40-4ed1-9b5e-a12007b0e694", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "9338436", + "uri": "http://javon.name" + }, + { + "id": "5d850498-1b27-485e-8359-b6182bd02f5a", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "5935690", + "uri": "https://dayton.name" + }, + { + "id": "1df4f0dc-f834-4b4a-8b99-48e45a5f219a", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1944756", + "uri": "http://ellis.info" + }, + { + "id": "906aec65-e9b4-4840-8be8-3c7f4f5f35ff", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "60166f39-6652-4366-a1b7-7eeb72860637", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "7219520", + "uri": "https://janae.name" + }, + { + "id": "116b21c4-cc87-4f61-8510-ebdef7522250", + "created": "2020-05-13T06:11:21.361Z", + "updated": "2020-05-13T06:46:15.893Z", + "createdBy": "tc-Admin", + "updatedBy": "tc-Admin", + "isInactive": false, + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8819322", + "uri": "http://jaden.net" + } +] diff --git a/scripts/db/data/Organization.json b/scripts/db/data/Organization.json new file mode 100644 index 0000000..62dc93d --- /dev/null +++ b/scripts/db/data/Organization.json @@ -0,0 +1,18 @@ +[ + { + "id": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "created": "2020-05-05T11:01:31.334Z", + "updated": "2020-05-05T11:02:10.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "name": "Topcoder" + }, + { + "id": "6a21394e-1278-4835-9e4d-cb4ff151fcd3", + "created": "2020-05-05T11:01:31.334Z", + "updated": "2020-05-05T11:02:10.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "name": "Cool Party" + } +] diff --git a/scripts/db/data/OrganizationSkillsProvider.json b/scripts/db/data/OrganizationSkillsProvider.json new file mode 100644 index 0000000..c9ec16b --- /dev/null +++ b/scripts/db/data/OrganizationSkillsProvider.json @@ -0,0 +1,11 @@ +[ + { + "id": "909da0a2-3c46-4ec9-9a6d-f6f3829fa07a", + "created": "2020-05-05T11:01:31.334Z", + "updated": "2020-05-05T11:02:10.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d" + } +] diff --git a/scripts/db/data/Role.json b/scripts/db/data/Role.json new file mode 100644 index 0000000..449fb2a --- /dev/null +++ b/scripts/db/data/Role.json @@ -0,0 +1,18 @@ +[ + { + "id": "8607ddb3-abf6-4512-a618-c60d4771174b", + "created": "2020-05-05T10:55:53.914Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "name": "Admin" + }, + { + "id": "e2a52b94-7d59-473c-b0ba-4e2c8f78f3d9", + "created": "2020-05-05T10:42:37.002Z", + "updated": "2020-05-05T10:45:12.100Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "name": "Admin02" + } +] \ No newline at end of file diff --git a/scripts/db/data/Skill.json b/scripts/db/data/Skill.json new file mode 100644 index 0000000..a0e8da1 --- /dev/null +++ b/scripts/db/data/Skill.json @@ -0,0 +1,46 @@ +[ + { + "id": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "created": "2020-05-05T11:03:56.711Z", + "updated": "2020-05-05T11:04:25.798Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "name": ".NET Framework 4", + "externalId": "KS126XR63RKYVCKYDNBN", + "uri": "http://www.google.com" + }, + { + "id": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "created": "2020-05-05T11:03:56.711Z", + "updated": "2020-05-05T11:04:25.798Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "name": "C++ (Programming Language)", + "externalId": "KS1219W70LY1GXZDSKW5", + "uri": "http://www.google.com" + }, + { + "id": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "created": "2020-05-05T11:03:56.711Z", + "updated": "2020-05-05T11:04:25.798Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "name": "Angular (Web Framework)", + "externalId": "KS120H6772VQ0MQ5RLVD", + "uri": "http://www.google.com" + }, + { + "id": "6df99fc1-3115-4b0c-bf2b-21ecd52fa64b", + "created": "2020-05-05T11:03:56.711Z", + "updated": "2020-05-05T11:04:25.798Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "name": "Python (Programming Language)", + "externalId": "KS125LS6N7WP4S6SFTCK", + "uri": "http://www.google.com" + } +] diff --git a/scripts/db/data/SkillsProvider.json b/scripts/db/data/SkillsProvider.json new file mode 100644 index 0000000..b567a3c --- /dev/null +++ b/scripts/db/data/SkillsProvider.json @@ -0,0 +1,10 @@ +[ + { + "id": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "created": "2020-05-05T11:02:49.718Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "name": "EMSI" + } +] diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json new file mode 100644 index 0000000..c8d2490 --- /dev/null +++ b/scripts/db/data/User.json @@ -0,0 +1,255 @@ +[ + { + "id": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "tc-Admin", + "firstName": "John", + "lastName": "Doe" + }, { + "id": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699", + "created": "2020-05-05T10:18:03.882Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "handle": "lazybaer", + "firstName": "Christopher", + "lastName": "DeLaurentis" + }, { + "id": "3f64739e-10bf-42ca-8314-8aea0245cd0f", + "created": "2020-05-05T10:18:03.882Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "handle": "TonyJ", + "firstName": "Tony", + "lastName": "J" + }, { + "id": "ecec4ad8-3a1d-4646-8641-25054e8f2d33", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Bernadine17", + "firstName": "Elmore", + "lastName": "Sanford" + }, { + "id": "7d93ee11-b4e9-415e-9a6f-aff458d6f975", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Drew54", + "firstName": "Jonathan", + "lastName": "Lueilwitz" + }, { + "id": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Marvin_Weimann68", + "firstName": "Naomie", + "lastName": "Rogahn" + }, { + "id": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Olga4", + "firstName": "Fernando", + "lastName": "Weimann" + }, { + "id": "0f8e52c1-33fd-48f2-b160-415c2bb371f2", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Theresia.Ledner29", + "firstName": "Katelynn", + "lastName": "Casper" + }, { + "id": "06f130d0-0764-4894-bcd8-67e2758b15d9", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "America6", + "firstName": "Garrett", + "lastName": "Boyle" + }, { + "id": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Rollin_Stiedemann45", + "firstName": "Jakob", + "lastName": "Kris" + }, { + "id": "35ec01c0-d650-4cd9-8a05-848c9019873d", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Verna.Stracke", + "firstName": "Sandy", + "lastName": "Davis" + }, { + "id": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Demond39", + "firstName": "Domenic", + "lastName": "Casper" + }, { + "id": "28df7acf-d7b1-467c-8ee5-594c7bace8dc", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Sylvan_Gorczany", + "firstName": "Samara", + "lastName": "Schultz" + }, { + "id": "ef2498f9-7046-4fad-ad85-fc5e4675e693", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Dana_Paucek6", + "firstName": "Carroll", + "lastName": "Vandervort" + }, { + "id": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Pauline_Bernier", + "firstName": "Sandra", + "lastName": "Fay" + }, { + "id": "3f8d1ed0-531a-4695-bfc9-f7beda034a66", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Tavares_Bechtelar95", + "firstName": "Brady", + "lastName": "Swift" + }, { + "id": "05301625-4a45-4cf9-b93c-cd6b55ed9f74", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Adrian.Stehr23", + "firstName": "Reymundo", + "lastName": "Armstrong" + }, { + "id": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Lourdes0", + "firstName": "Larissa", + "lastName": "Sporer" + }, { + "id": "cdaeb417-e400-4df1-b484-f99ae10b4800", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Leilani_Fahey35", + "firstName": "Godfrey", + "lastName": "Morar" + }, { + "id": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Jaclyn47", + "firstName": "Braeden", + "lastName": "Kuhic" + }, { + "id": "6fa6d708-68a6-47be-9591-4b5100921b3a", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Kian.DuBuque", + "firstName": "Myles", + "lastName": "Connelly" + }, { + "id": "f8bf2ba2-0e21-47c7-9410-4596b61c6403", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Adrain26", + "firstName": "Maryam", + "lastName": "Satterfield" + }, { + "id": "5bd69a82-c2cb-476f-9462-0883d3b28b90", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Sandrine_Kuvalis98", + "firstName": "Mekhi", + "lastName": "Tremblay" + }, { + "id": "428a5d57-558c-4387-bada-6c966eb3b4bd", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Rebeka75", + "firstName": "Bud", + "lastName": "Kihn" + }, { + "id": "6910d2f4-a50a-4494-8f46-6de1f3d032c2", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Aditya65", + "firstName": "Clemens", + "lastName": "Rodriguez" + }, { + "id": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Alexandra_Mertz85", + "firstName": "Chadrick", + "lastName": "Shields" + }, { + "id": "60166f39-6652-4366-a1b7-7eeb72860637", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Laisha_Terry86", + "firstName": "Augusta", + "lastName": "Torp" + }, { + "id": "bdcb113f-6715-40fd-8dab-14aa01327ae9", + "created": "2020-05-05T10:33:03.470Z", + "updated": "2020-05-05T10:40:48.900Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "handle": "Ida.Ankunding", + "firstName": "Mckenna", + "lastName": "Collier" + } +] diff --git a/scripts/db/data/UserAttribute.json b/scripts/db/data/UserAttribute.json new file mode 100644 index 0000000..5389d7a --- /dev/null +++ b/scripts/db/data/UserAttribute.json @@ -0,0 +1,2242 @@ +[ + { + "id": "20721207-c868-4ee4-8ac4-59db683b7bce", + "created": "2020-05-13T08:48:30.541Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "efcbd4a7-4e1c-4109-8fac-50a1d6d5b7df", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "London", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "cd28194f-c3a9-4445-bef0-ea411996cc93", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Bulwark International Inc", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "f3be8fee-26ce-411c-92ae-381815c15258", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Senior Consultant", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "90fef208-619d-49c6-890e-c789664b28d7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "44312121", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "8a174a2b-cda3-463d-b3ef-41367d983258", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "17ff7dcb-8751-4fe1-bb3c-c2b00b447d3c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "42", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "15e7bcf1-aac7-4718-ac11-6f2b94be3d33", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "f15010ef-76fd-4a70-aa7c-a53200300300", + "created": "2020-05-13T08:48:30.541Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "208386ea-b3d7-4add-ab6c-6460b0ae7fab", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "New York", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "edb933cc-e2fb-4112-a2fc-6809af7733c5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Rhodes Fun Company", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "143d5d73-cde5-4f88-a357-666eb219b822", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "IT Support Specialist", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "21de9324-900d-41ea-b127-f297dfb9a873", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "74314457", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "b93ebc48-8d24-4ea9-8d4b-08fd00b20901", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "cfd55fff-4b21-48ce-bcc6-fd031f20ebe5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "3", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "86b5945d-287b-462a-9a47-4e506f03f2e6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "e1d7746c-5934-47a6-abae-1287209a5574", + "created": "2020-05-13T08:48:30.541Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "19890796-ae45-4e0b-bcb3-e039e8a09ff3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "New York", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "28673288-0c31-4a5d-bbd1-8e9dcaf40e51", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Rhodes Fun Company", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "0b4c80aa-4e1b-4e07-ab84-087119f05105", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "IT Support Specialist", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "5ff8c983-69dc-4471-bab5-0eb11b6296e2", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "41331212", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "3d52eda6-8195-4ae3-9c59-7807e3c175ef", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "e3527b06-27e5-409d-a019-195ae564002b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "120", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "8fbbecb4-ca4e-4cc1-9f98-bbccb925b034", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f" + }, + { + "id": "5dd37cce-db54-4536-a001-b8c094815d13", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "ee6906c3-8934-4f1c-b34d-c43de54e5332", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Maggio - Huel", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "8040621a-f39e-4566-a1b5-6eb8fbcd0b12", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "East Carmen", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "c5850fc6-573c-4551-8808-1058e26b9cd4", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Global Factors Manager", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "cca9edf1-6c32-4f11-b1c1-ee293c91d488", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "21218800", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "2fc675da-2574-4aad-a810-3caf90ba05f3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "d43bc932-c846-442e-b37a-44e049c05e3d", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "111", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "bf4e8a5e-c53c-4420-b3c7-eae0fd5ffc14", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "426ad8db-5b5e-4a8f-98e5-11bc77d8d1b9", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "11255ef8-76f7-45b3-9289-92c198dfcbc8", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "White and Sons", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "68d78284-87b3-4ef9-9883-5279370bebfe", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Savanahville", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "f7b43e5f-c122-4116-92e3-b542df48213b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Legacy Optimization Administrator", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "e0026c2c-f2f9-439e-8cd0-81253d66f0d3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "24329660", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "10179125-e302-44ca-8d33-edde6b3abfcb", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "2675e749-4627-411d-96f8-fc3123b6b73d", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "64", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "a24f589a-608e-46e1-a58f-c4adea5bffd6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "403b83a9-ffcd-41cb-ae9a-605803230830", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "34bd4399-00ae-4183-94e9-f5a428a2208b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Mitchell - Cormier", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "d1e3f110-ea4c-440b-8be5-d42d609f33b7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Lake Audra", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "0ac8883a-e538-41d4-b582-811ab12d6edf", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "District Intranet Producer", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "85cdc151-8b7a-417c-9fbb-d80341d660ec", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "14990140", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "eb914bc7-0107-4833-a1fd-daf31577ad0e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "3c046058-de89-4c31-9572-90552da08c8a", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "62", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "4f3e4daa-c8b7-4a06-a27c-f7ad5bfadb56", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "0e4a6bff-c4e3-4dfd-aaa6-08b92eaa8899", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "774bc0c4-6c01-46e6-9675-7fb09676a3ed", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Gleichner LLC", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "274625ff-9b01-4c42-b6f5-bfa9e261ec53", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Elainaville", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "1eb5e3d1-8f7a-4ab1-8ad0-0c6e117211a9", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Customer Security Supervisor", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "61c557c3-6c2b-4df9-b532-cb1a2eafe5b4", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "45708781", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "22bdda9f-547c-4271-9775-8cc5fcafac1a", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "5f0b1883-0f8a-47f5-a9e2-7f389aef5b11", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "86", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "5509de53-f7fb-41a2-8632-a1bbe332cdca", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "1d806950-ad88-46ce-ada4-89cf0ef53514", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "0312267c-84e1-4fbb-a9b4-abb1652575ed", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Gottlieb - Stamm", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "23012b9a-c77e-40a8-81ee-e001509b96c3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Howellstad", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "49c0d0a6-b435-409f-bc04-e16efeb61d22", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Dynamic Paradigm Consultant", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "b54672a0-547d-414f-b26f-f781758072c1", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "70768145", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "3162965b-2637-42f1-91d5-2ce6f119dce9", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "133b0c3e-72ae-4f1b-8845-dc3f96d02997", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "16", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "ed72468f-e6d3-455b-b17b-d92fe2b048a1", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "328e017e-ddf9-419e-9d75-8d753c00d771", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "5a365cc8-3ee8-4d9c-be79-afc1f1cdbd2e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "McClure, Sipes and Gleason", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "94f46cc5-33eb-4eb8-88e3-65d7b473eaed", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "West Reneefort", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "a6556642-9e42-4d34-aab9-c861722e72db", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Internal Infrastructure Architect", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "572371a7-0a77-41fb-a55b-c73a95c39c25", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "37263884", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "cbde1ea9-aa28-48b7-a441-8124b6bb0069", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "9cd21079-5fff-46f8-95e0-e9ba3be1e9b5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "119", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "a0b829f6-770a-4699-a4ea-7a94f6f2d7b5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "b966a529-c6d8-4818-b468-1f202c6ece67", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "ed88eaad-4dda-4616-94b3-9bfcc0c226af", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Robel - Schamberger", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "6f4ba6a0-4d53-4169-a00e-051ef25bd567", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Vanside", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "bf7f66cf-f90e-4902-9c5f-86637fa5ef3e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "District Solutions Orchestrator", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "4c5a2f1b-cc9b-4ebb-b90a-1cdaf0a9e22f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "33923288", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "064f9dcf-cbea-4e6d-bc7c-5ccd8a86d140", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "c21de0af-a57d-4d7b-83b4-7a5436b09cf8", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "61", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "0a3f5fc4-ce69-43f6-b271-54d488f30d3c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "7a898c3c-9f9e-4930-aac4-faa9ca2799d3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "27810f02-829d-4bdb-b4e9-ce3b6939ac63", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Kris and Sons", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "12b1f4a2-2d45-4fe3-a499-b7e66fe9b112", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "East Jonatan", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "c7e72894-b8d1-4c44-94ab-720def208e32", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Direct Interactions Developer", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "49a2cb25-c038-40f8-b2b6-da398f66baca", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "77157857", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "6d5a49c0-6835-46bc-98b2-319db16cc4a2", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "807f50ba-dc75-4bfd-96c6-d929dbabdf72", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "68", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "0104a349-5c3b-46b1-8a02-27d916fae47f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "701fadd3-9c96-4056-b7b8-2bc81a29e98c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "1def20a9-c8fc-4a0a-9ad1-00b5099d0147", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Schmeler - Luettgen", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "9afe06b8-d32a-4a6d-a05f-8bf5d45190f6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Gottliebton", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "fefdd210-2639-4c0e-bb94-0d31af22e013", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Dynamic Solutions Supervisor", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "f1a02ceb-b084-4298-9a7f-6d2168fea701", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "33295693", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "cc8b0eaf-8cde-4720-9a4f-bf00ac6dfdf7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "c22de447-f048-4ab4-bdb7-2b5f91005118", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "47", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "a40da9f2-3bd5-4cc5-9f2a-8dd28ba439c0", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "2a66dfe9-200b-4f80-9797-c871b0c1b1a5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "046734c6-9f42-4161-80eb-cc1d5d7e6c24", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Armstrong - Huels", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "0078cbc9-86f6-4519-a76a-887e91a8af47", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Ervinchester", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "af4ab82b-f88e-4bc1-8790-8fb883866e51", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Internal Integration Engineer", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "cdd8156e-8866-4c9b-970b-e05c606f6070", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "99775367", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "f307e359-40ed-475b-99d4-88a6737751b9", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "cfcee09e-e52e-4e63-bdd5-567de46f0def", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "27", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "44a195d8-d00f-49ef-b949-c47b652ff031", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "98fcc329-0bb5-403e-85eb-7e385ced0925", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "edb0592b-9c3a-439f-9f64-5f58655f40d3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Huel LLC", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "df113baa-8eaa-469c-be82-3ed144071319", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Matteoburgh", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "fe1f803a-7933-4259-8add-f642253db09f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Principal Tactics Executive", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "42218085-8547-4a99-b48d-1dba7cdf016f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "66713622", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "51e028a6-88c7-48b6-82d3-f4465b1cbf34", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "1c8cab2b-3a2e-4ff0-97d2-dc0e579171e6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "70", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "436affcd-3e37-45cb-8cd3-31b65fd1fd72", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "985d3d22-c079-475e-b73e-0f9cd2cd3afe", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "bd6277a3-e2e4-4044-bf77-e84558ca2543", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Friesen - Ward", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "0fa99b42-adf8-4df2-95f0-2459e913c283", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Curtmouth", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "91b3b210-71ca-4965-9e74-764ac1fd6ced", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Dynamic Creative Architect", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "99149bbd-f53d-4950-91a2-15ab3b694eb4", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "55925984", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "b0730f33-4b50-4a5b-9c8a-30ebc58879e1", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "10e26bb7-2e33-4fa6-9366-576054fc5421", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "81", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "f45d0017-7160-4dcf-b198-35ad7111c66b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "4c1ace0a-dbf8-4d66-a227-ae91649dab8d", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "8c970824-93f6-4569-99fd-e6b089c6705f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Watsica, Pouros and Hintz", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "45c9eb7b-05aa-40c6-b812-c908e0a538ea", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "North Raphaelleton", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "0c5c5d3e-e9f6-4774-8952-6d0c9dbd3727", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Forward Usability Manager", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "89d6e550-9146-481a-8d61-ddb9d7b30314", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "23132031", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "1833191c-c47b-4109-8458-823941e21663", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "da5b3716-a0a4-4bcb-a939-accf2589feeb", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "99", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "c85c057c-9018-4ddb-adf5-6617c368fb1a", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "6b79a0ad-1072-429c-8635-0605f8338193", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "5555b654-ab55-4309-9375-9f39a54ad29f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Lueilwitz - Towne", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "2ad3dd8e-b2d1-49a5-b87a-73ce327fe7ec", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Laishaside", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "72f33d91-f448-42dd-a2ac-a15e5e4b70b5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Human Interactions Orchestrator", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "7c3cc565-f639-4835-bad1-c70c52e8ee71", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "75367167", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "ff57462d-8fbe-4b8f-a4cf-9909e6d09481", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "a606d82a-618e-42c6-9453-25f6ff68b870", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "115", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "eb5ecfab-094a-4032-b0fd-c2d82316e3b1", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "f2e19b83-7f89-45e3-a717-6c3e3ad09ce3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "7ee559fe-8278-4aa1-86cd-037fa64af613", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Nienow, Wilkinson and Orn", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "cbec7b35-3a1d-497d-b8e0-c11d16909913", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "West Trystanmouth", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "d3b2c1a1-4828-409c-b66e-073f284dcee0", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Customer Creative Liaison", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "66accc53-815b-4fae-b14e-9b62274f412e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "25337335", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "c8859ce8-bf7a-4ee6-94f9-215dda3c56f6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "2543a68d-89b1-4dd7-ba0f-97df4983493e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "63", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "14d1daa9-2b6d-493d-91cd-213e7142915c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "a3f2faea-dca7-4414-b710-ea1d06593ff3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "a224782f-a8d6-4d6c-9c40-e21e3754c6bc", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Hills - Kuphal", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "d6752060-133d-4398-9317-6639b17780e8", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "West Torrey", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "03853ac4-65df-4743-a851-fc649ff839eb", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "International Accounts Administrator", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "a5407f50-e1d2-4910-ba43-8eb79954c1ec", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "26014689", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "ad86ca65-2c1f-41ed-87aa-90db3f13474c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "2ecc586f-8d7a-4b77-ab00-3f2157cb4a95", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "93", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "f5a85931-3fe1-423c-824b-ae69f4ac54b5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "c04b0823-3e0f-47b5-a268-abb0264d7b60", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "c3117be5-170a-47a5-b105-5e1c912b3b70", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Barrows Group", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "8c8ecb61-c301-44f5-8dce-e4cafdcb4104", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Abernathystad", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "fcc54750-d625-490b-b2fe-4f4f33abd225", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Principal Functionality Representative", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "cf3a6088-d365-4e02-907a-1b9d31ee4a22", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "38578266", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "95ef3596-561e-43cf-a0e1-3b7311513040", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "7ac5402b-6164-4fa3-a43b-8922e415c6d3", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "50", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "dbdd0eb4-eadc-49ed-b162-1ad4351c968b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "ce7fcfc1-4298-438e-86f0-ac489223d942", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "4c4ada3f-0cfa-4f74-80e4-8a49f1c3d071", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Schuster, Russel and Howell", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "284f1476-6f3d-4b52-beb8-93919354bd05", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Danland", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "c838929a-ba6b-484d-9260-fcb57d7bb949", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Chief Identity Director", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "47f82eaf-d700-42ff-bfcd-6734e85ff2e6", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "70382986", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "be1aab9b-24e9-41b3-9ed4-9dd9b4326dbc", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "f31c173c-6f1c-4700-89c9-d6a2af96bd22", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "10", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "199276d1-3ea4-4e07-984b-93ba6a6c1150", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "658fd5b9-a324-423b-95e9-bedf1903a339", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "6f10fe2a-e263-4b8a-827e-417e73568649", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Kohler - Satterfield", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "c15a26da-61b2-42d4-8dac-4ebd899502eb", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Lednertown", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "9378dfda-2e83-446d-b15b-37663c9dfa69", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Investor Intranet Strategist", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "bc6f9826-0372-4f45-a629-2edf126c6da7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "52530174", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "094f6747-428c-44ec-a211-35152cf0b743", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "d8b364e8-c3bd-47a2-860d-a8e8dac79694", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "9", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "739cb482-1b35-42b6-8367-091460f40a82", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "15de9040-de71-4eb1-afd0-b3355df8991f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "944ebee5-2f84-40fb-8dd9-39a168da3deb", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Ortiz Inc", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "446a4d82-3875-4b0b-979a-c379d1912e89", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Athenamouth", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "3c48f597-96c9-4350-a9e2-2d4579d270e9", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Central Mobility Manager", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "261eb9e0-d15a-4264-aaf8-e1bfa48fc71e", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "32047233", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "26380037-fff9-4f4b-bb0a-ba6286916e57", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Female", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "86918bb6-b3d1-47fb-8488-cb71292aa462", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "47", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "205f44d4-d320-45f9-85d6-40e45be9517c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "cd30641a-61fb-43f6-a756-5c9c7886099b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "ad4720e2-7521-49f7-91c5-1f74023dd7a8", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Walsh - Gibson", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "ab60ac19-9293-4408-b676-6d256bd264a5", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "North Abagailport", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "fc2993a7-806c-40f2-8152-369c45269a5d", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Dynamic Functionality Facilitator", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "b7d1718f-9ecd-4d0c-a8e7-02e9c869fb6b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "72206031", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "38da227c-f95a-4f4c-8b41-b75bddf98efc", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "a1934fce-4ff2-43b9-8f14-0fdeae943eff", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "119", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "8a22305f-87a2-4d01-856e-919950809412", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "f545da95-47a2-4d2b-a9f2-1031b8f6fafd", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "78806580-3e21-4aca-8bb5-2e4257bce5ae", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Hamill - Kuhn", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "1557d031-4dfb-4997-93d1-bd8312d13b5f", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "North Andres", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "185f210b-0747-49af-91de-86691258e5c4", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "International Group Designer", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "a41cfa28-99f3-4d77-87bf-452c18852a15", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "05112487", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "9a17c5c8-f386-4d78-9208-23f7c0253872", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "b6f3fedf-7b94-4bec-ad35-dddcc20864ad", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "120", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "e86638bd-3c48-4ac2-bec5-7e87cda430c7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "e07ecbb1-7fe3-4bb6-87ad-cb681f6b81bd", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "false", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "82b7937d-f86c-4843-a14b-90d5f3f810cc", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Reichel Inc", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "4489b9ab-9b21-431c-86ba-8b6b7d0d695c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "New Herbert", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "980e83ec-68c0-44ee-9e81-014d0ad7f434", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Lead Configuration Orchestrator", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "5c9230a3-0f91-4549-9a48-43958cb1f5a4", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "97879088", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "694ee4e5-372e-4632-bde2-dd10b5a49167", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "7207d822-2cff-475e-a9ef-735c50c884d2", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "105", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "9a0ca87a-0f2c-4a47-a30b-18af9f006069", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "Yes", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "df7f0199-9f6e-4660-ad70-016d03f3f7dc", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "c650c24c-63d3-46c7-9c04-9605742ab4a7", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Armstrong LLC", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "61ec7b6e-0e48-402d-a668-0b3e9fe40e5b", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "Bergstrombury", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "62211257-eccd-4d85-82f8-85fab58434e1", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Future Optimization Planner", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "c468e5fe-4d12-4fbc-94f6-f38fbf95b675", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "94537520", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "926c51bd-b0a1-4ed9-911c-90c0b12e49bf", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "56de9e67-a757-4dbb-8fe7-e49bc0eb6514", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "54", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "81a27a4f-8517-4dee-ac2c-a9494465426d", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "112fa1b7-3cc1-4ff1-ac71-706e86cb6591", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "value": "true", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "aafe288c-e291-45f3-ab6e-2b785d3cc763", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "value": "Armstrong LLC", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "fbb00073-a54b-44dd-8a74-9e8eb6e594e2", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", + "value": "West Santinoside", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "6927a03c-c538-403b-be93-e4a773107fc0", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", + "value": "Legacy Research Coordinator", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "b8bb7dec-2536-4d80-b2bd-f992f709a4ea", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "d709276a-80c3-491c-9b29-a4f065b2a56f", + "value": "46152109", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "6f2aec53-786f-48f2-a040-3167b3a3a86c", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "09a51576-91ab-4f8e-ac34-36c19d8f4390", + "value": "Male", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "48f3258e-37f8-47e6-b212-c4862b90e2da", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7b524afa-69ac-49d5-a624-701e4cdc4e92", + "value": "8", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "115a9ef1-a425-4584-8df5-432bb14da532", + "created": "2020-05-13T08:19:13.709Z", + "updated": null, + "createdBy": "tc-Admin", + "updatedBy": null, + "attributeId": "7bfbf2a8-42bc-4ac0-a90e-ab4b13658d32", + "value": "No", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + } +] diff --git a/scripts/db/data/UsersRole.json b/scripts/db/data/UsersRole.json new file mode 100644 index 0000000..b1e4443 --- /dev/null +++ b/scripts/db/data/UsersRole.json @@ -0,0 +1,11 @@ +[ + { + "id": "977d66bb-112f-49b1-8b20-2ef7a17f4943", + "created": "2020-05-05T11:00:05.664Z", + "updated": "2020-05-05T11:00:38.750Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "roleId": "8607ddb3-abf6-4512-a618-c60d4771174b" + } +] \ No newline at end of file diff --git a/scripts/db/data/UsersSkill.json b/scripts/db/data/UsersSkill.json new file mode 100644 index 0000000..c0c224e --- /dev/null +++ b/scripts/db/data/UsersSkill.json @@ -0,0 +1,638 @@ +[ + { + "id": "952d5f60-153c-466e-b4e9-8d5b91cc78c4", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "dab98c8c-8bdc-4946-9a47-4f972540f88f", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "9027f0ed-0fcf-415b-8c18-a9d22acaa83f", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "id": "35ec5d07-81c8-4087-9ab1-ac5d56ae9685", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "0253fe94-5436-4235-b630-8b04038da58b", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "6df99fc1-3115-4b0c-bf2b-21ecd52fa64b", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + }, + { + "id": "f7a1348c-2b66-45e2-9754-63174f73ef54", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ecec4ad8-3a1d-4646-8641-25054e8f2d33" + }, + { + "id": "f5a04415-d077-480d-b07a-b34411000f6b", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "5fc0e9b5-9c59-4a52-9c35-c40843e700a3", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "7d93ee11-b4e9-415e-9a6f-aff458d6f975" + }, + { + "id": "ff3f1cb6-41d9-4442-ad60-fa8a691d5157", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "67a21eb4-cab6-46f3-b5a2-b0d701d281a4", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "df2f0027-f74f-45fa-85cd-84c9fdc2faf4" + }, + { + "id": "580268e9-63b7-4f11-a390-11ce877a7612", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "2fee1d79-8cb3-4ab2-b09a-876d7de7415d", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e043286d-ab55-44e3-b2c2-7f7a4f375dcf" + }, + { + "id": "9289d3e0-48da-4385-8e02-c297ea115509", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "d0b10d6c-54b4-4227-a575-c31d67662f64", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0f8e52c1-33fd-48f2-b160-415c2bb371f2" + }, + { + "id": "c3a5e3f6-ba3f-4318-90fb-d037a2671e08", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "03d6c846-4527-44f3-a17d-00fca942c5ea", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "06f130d0-0764-4894-bcd8-67e2758b15d9" + }, + { + "id": "db8793c0-63a9-4e65-9585-9ca5fc338cc1", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "072a2cf4-9261-45dc-9e62-c4159713ac8d", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e02d66c9-01d6-4cd8-9f8c-30e40315adcc" + }, + { + "id": "f52a40d3-9539-4060-a7e3-2bb902fb3146", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "35ec01c0-d650-4cd9-8a05-848c9019873d" + }, + { + "id": "2a3a3de2-b4c0-4943-8f05-dfa57878e13d", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "4f2dc463-e24b-4b4a-8cde-c0122fbfb8ac" + }, + { + "id": "87c2b813-b7e9-4ed6-9278-559e3776840c", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "28df7acf-d7b1-467c-8ee5-594c7bace8dc" + }, + { + "id": "30baa99d-138c-4a9a-8a31-6d5233a156c6", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "a18f2876-0afb-4fcd-825c-2e596532ca63", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "cc1e6589-3a24-414b-a49d-8b9232873860", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ef2498f9-7046-4fad-ad85-fc5e4675e693" + }, + { + "id": "6c0af7ea-1fcf-4644-abe9-05283834839c", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "0be1ba83-e5d6-4c5a-9e0a-597d12bd8aa0", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "e0312bf4-aa2f-401a-9871-9094e4f66c63", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "f0df47fc-2b1f-44ad-bd38-1ada036ba4d9" + }, + { + "id": "24bdaa39-2737-444f-97d7-e1e973507485", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "3f8d1ed0-531a-4695-bfc9-f7beda034a66" + }, + { + "id": "53240145-d2da-4774-87f7-2fdb41c12783", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "887f8cf7-da4a-4c82-89a9-7a6ebccceb46", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "7d91798e-69b9-4fd8-8742-b23ad75c8de2", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "05301625-4a45-4cf9-b93c-cd6b55ed9f74" + }, + { + "id": "404402d8-2d70-4eb6-b7c2-e1d2ff1ab367", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "8ce9125c-ca88-43b9-88e4-aef694cfa51a", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "460bddcd-3580-4f2a-bfe8-5ba6d8f6f6af" + }, + { + "id": "474e372a-12b7-43c6-8864-32fc542efc3f", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "61ccfcfd-76f8-43de-ac76-f93a97742358", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "cdaeb417-e400-4df1-b484-f99ae10b4800" + }, + { + "id": "9f2977f3-37e6-46e7-ab24-2f19cd5805b0", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "b26c9535-54ee-4181-80c8-4a17a7391bc6", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "e283eb2e-bfb7-43a6-a06d-ed89af338a4f" + }, + { + "id": "5d26b870-4da1-4577-8ab0-fd1ae39bd2a3", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "03a8309c-2f79-4c12-bd25-7229aa4700ce", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "39108d03-f358-47a7-b33f-7144a1f08f7d", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6fa6d708-68a6-47be-9591-4b5100921b3a" + }, + { + "id": "dc76abdf-8dd8-469e-86f7-176694cce03c", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "675e7924-4771-4aec-a54a-d12d56b37e80", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "f8bf2ba2-0e21-47c7-9410-4596b61c6403" + }, + { + "id": "d3d61aef-c40b-48e6-ab52-c701f8922fa3", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "5bd69a82-c2cb-476f-9462-0883d3b28b90" + }, + { + "id": "1b2c64af-40da-4232-84ac-85eec3326521", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "428a5d57-558c-4387-bada-6c966eb3b4bd" + }, + { + "id": "1147f0c5-c1d5-4bc6-b259-e3a3d6c4226a", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "b3924ef7-f3b7-438b-9b3c-e023a017839c", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "188eb543-1c44-43ec-a890-e68bda0a80d7", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "6910d2f4-a50a-4494-8f46-6de1f3d032c2" + }, + { + "id": "01e70d17-60a8-4116-a023-4fa8a71f05b0", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "e8d8c734-9a69-47ed-a4d8-2512ab5e6ad2", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "7e3f962a-378a-4f18-9cc7-ffbcff7f9b35" + }, + { + "id": "487bf55b-1bc5-4533-bedf-9af70f1622f6", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "60166f39-6652-4366-a1b7-7eeb72860637" + }, + { + "id": "3ee478a1-6385-41d8-ac35-e2f6dd5729e5", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0de5cb8b-d217-4133-8483-a3a09db9ab3d", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "bad98901-4d06-4694-bdef-a4243ae77dc0", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "0aec2956-cbcb-4c80-8c00-25cc02a71611", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + }, + { + "id": "0a916b78-2cc1-46cb-8dea-970e531b09aa", + "created": "2020-05-05T11:04:51.134Z", + "updated": "2020-05-05T11:05:13.574Z", + "createdBy": "tc-Copilot", + "updatedBy": "tc-Copilot", + "skillId": "ab8f01fc-9686-4cc1-9b59-c412b4bae3f2", + "metricValue": "4.5L", + "certifierId": "certifier_id", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9" + } +] diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js new file mode 100644 index 0000000..cef1924 --- /dev/null +++ b/scripts/db/dropAll.js @@ -0,0 +1,35 @@ +/** + * drop tables + */ +const _ = require('lodash') +const models = require('../../src/models') +const logger = require('../../src/common/logger') +const { topResources, modelToESIndexMapping } = require('../constants') +const { getESClient } = require('../../src/common/es-client') + +async function main () { + const client = getESClient() + const keys = Object.keys(models) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (models[key].tableName) { + const esResourceName = modelToESIndexMapping[key] + try { + if (_.includes(_.keys(topResources), esResourceName)) { + await client.indices.delete({ + index: topResources[esResourceName].index + }) + } + + await models.DBHelper.clear(models[key]) + } catch (e) { + console.error(e) + logger.warn(`drop table ${key} failed`) + } + } + } +} + +(async () => { + main().catch(err => console.error(err)) +})() diff --git a/scripts/db/genData.js b/scripts/db/genData.js new file mode 100644 index 0000000..2f501a1 --- /dev/null +++ b/scripts/db/genData.js @@ -0,0 +1,146 @@ +const _ = require('lodash') +const models = require('../../src/models') +const logger = require('../../src/common/logger') +const { getESClient } = require('../../src/common/es-client') +const { + topResources, + userResources, + organizationResources, + modelToESIndexMapping +} = require('../constants') + +async function insertIntoES (modelName, body) { + const esResourceName = modelToESIndexMapping[modelName] + + if (!esResourceName) { + logger.error(`Cannot insert data into model ${modelName}. No equivalent elasticsearch index found`) + + return + } + + const client = getESClient() + + if (_.includes(_.keys(topResources), esResourceName)) { + await client.create({ + index: topResources[esResourceName].index, + type: topResources[esResourceName].type, + id: body.id, + body, + refresh: 'true' + }) + } else if (_.includes(_.keys(userResources), esResourceName)) { + const userResource = userResources[esResourceName] + + const user = await client.getSource({ + index: topResources.user.index, + type: topResources.user.type, + id: body.userId + }) + + if (userResource.nested === true && userResource.mappingCreated !== true) { + await client.indices.putMapping({ + index: topResources.user.index, + type: topResources.user.type, + include_type_name: true, + body: { + properties: { + [userResource.propertyName]: { + type: 'nested' + } + } + } + }) + userResource.mappingCreated = true + } + + const relateId = body[userResource.relateKey] + + if (!user[userResource.propertyName]) { + user[userResource.propertyName] = [] + } + + if (_.some(user[userResource.propertyName], [userResource.relateKey, relateId])) { + logger.error(`Can't create existing ${esResourceName} with the ${userResource.relateKey}: ${relateId}, userId: ${body.userId}`) + } else { + user[userResource.propertyName].push(body) + await client.update({ + index: topResources.user.index, + type: topResources.user.type, + id: body.userId, + body: { doc: user }, + refresh: 'true' + }) + } + } else if (_.includes(_.keys(organizationResources), esResourceName)) { + const orgResource = organizationResources[esResourceName] + + const organization = await client.getSource({ + index: topResources.organization.index, + type: topResources.organization.type, + id: body.organizationId + }) + + const relateId = body[orgResource.relateKey] + + if (!organization[orgResource.propertyName]) { + organization[orgResource.propertyName] = [] + } + + if (_.some(organization[orgResource.propertyName], [orgResource.relateKey, relateId])) { + logger.error(`Can't create existing ${esResourceName} with the ${orgResource.relateKey}: ${relateId}, organizationId: ${body.organizationId}`) + } else { + organization[orgResource.propertyName].push(body) + await client.update({ + index: topResources.organization.index, + type: topResources.organization.type, + id: body.organizationId, + body: { doc: organization }, + refresh: 'true' + }) + } + } +} + +/** + * import test data + * @return {Promise} + */ +async function main () { + await models.init() + + let keys = Object.keys(models) + keys = _.orderBy(keys, k => { + const esResourceName = modelToESIndexMapping[k] + + // Create parent data first + if (_.includes(_.keys(topResources), esResourceName)) { + return -1 + } + + return 1 + }) + + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (models[key].tableName) { + try { + const data = require(`./data/${key}.json`) + await models.DBHelper.clear(models[key]) + for (let i = 0; i < data.length; i++) { + await models.DBHelper.save(models[key], new models[key]().from(data[i]), true) + await insertIntoES(key, data[i]) + } + logger.info('import data for ' + key + ' done') + } catch (e) { + logger.error(e) + logger.warn('import data for ' + key + ' failed') + } + } + } + logger.info('all done') + process.exit(0) +} + +(async () => { + main().catch(err => console.error(err)) +})() diff --git a/src/bootstrap.js b/src/bootstrap.js new file mode 100755 index 0000000..d4bf725 --- /dev/null +++ b/src/bootstrap.js @@ -0,0 +1,29 @@ +/** + * add logger and joi to services + */ + +const fs = require('fs') +const path = require('path') +const logger = require('./common/logger') + +/** + * add logger and joi schema to service + * @param dir + */ +function buildServices (dir) { + const files = fs.readdirSync(dir) + files.forEach((file) => { + const curPath = path.join(dir, file) + const stats = fs.statSync(curPath) + if (stats.isDirectory()) { + buildServices(curPath) + } else if (file.toLowerCase().indexOf('service.js') >= 0) { + let serviceName = curPath.split('modules')[1] + serviceName = serviceName.substr(1, serviceName.length - 4) + logger.info(`add decorators for service --> ${serviceName}`) + logger.buildService(serviceName, require(curPath)); // eslint-disable-line + } + }) +} + +buildServices(path.join(__dirname, 'modules')) diff --git a/src/common/controller-helper.js b/src/common/controller-helper.js new file mode 100644 index 0000000..a00dd76 --- /dev/null +++ b/src/common/controller-helper.js @@ -0,0 +1,136 @@ +const _ = require('lodash') + +/** + * get common controller service + * @param service the service + * @return {{patch: patch, search: search, get: get, create: create, update: update, remove: remove}} the common controller methods + */ +function getControllerMethods (service) { + const { injectSearchMeta } = require('./helper') + + /** + * create entity by request data + * @param req the http request + * @param res the http response + */ + async function create (req, res) { + res.json(await service.create(req.body, req.auth)) + } + + /** + * patch entity by id + * @param req the http request + * @param res the http response + */ + async function patch (req, res) { + res.json(await service.patch(req.params.id, req.body, req.auth)) + } + + /** + * get entity by id + * @param req the http request + * @param res the http response + */ + async function get (req, res) { + res.json(await service.get(req.params.id, req.auth, req.query)) + } + + /** + * search entities by request query + * @param req the http request + * @param res the http response + */ + async function search (req, res) { + const result = await service.search(req.query, req.auth) + injectSearchMeta(req, res, result) + res.send(result.result) + } + + /** + * remove entity by id + * @param req the http request + * @param res the http response + */ + async function remove (req, res) { + await service.remove(req.params.id, req.auth) + res.status(204).end() + } + + return { + create, + search, + remove, + get, + patch + } +} + +/** + * get sub controller common methods like usersSkill,usersRole + * @param service + * @return {{patch: patch, search: search, get: get, create: create, remove: remove}} + */ +function getSubControllerMethods (service) { + const { injectSearchMeta } = require('./helper') + + /** + * create entity by request data + * @param req the http request + * @param res the http response + */ + async function create (req, res) { + res.json(await service.create(_.extend(req.body, req.params), req.auth)) + } + + /** + * patch entity by id + * @param req the http request + * @param res the http response + */ + async function patch (req, res) { + res.json(await service.patch(req.params.id, _.extend({}, _.omit(req.params, 'id'), req.body), + req.auth, _.omit(req.params, 'id'))) + } + + /** + * get entity by id + * @param req the http request + * @param res the http response + */ + async function get (req, res) { + // req.query was added to pass query parameters to ES search in a separate parameter not to impact + // the existing DB code. + res.json(await service.get(req.params.id, req.auth, _.omit(req.params, 'id'), req.query)) + } + + /** + * search entities by request query + * @param req the http request + * @param res the http response + */ + async function search (req, res) { + const result = await service.search(_.extend(req.query, req.params), req.auth) + injectSearchMeta(req, res, result) + res.send(result.result) + } + + /** + * remove entity by id + * @param req the http request + * @param res the http response + */ + async function remove (req, res) { + await service.remove(req.params.id, req.auth, _.omit(req.params, 'id')) + res.status(204).end() + } + + return { + create, + search, + remove, + get, + patch + } +} + +module.exports = { getControllerMethods, getSubControllerMethods } diff --git a/src/common/error.middleware.js b/src/common/error.middleware.js new file mode 100755 index 0000000..5eb9430 --- /dev/null +++ b/src/common/error.middleware.js @@ -0,0 +1,30 @@ +/* eslint-disable no-unused-vars */ + +/** + * Common error handling middleware + */ +const logger = require('./logger') + +const DEFAULT_MESSAGE = 'Something is broken at API server-side.' + +/** + * The error middleware function + * + * @param {Object} err the error that is thrown in the application + * @param {Object} req the express request instance + * @param {Object} res the express response instance + * @param {Function} next the next middleware in the chain + */ +function middleware (err, req, res, next) { + logger.logFullError(err) + + if (err.isJoi) { + res.status(400).json({ message: err.details[0].message }) + } else { + const status = err.status || 500 + const message = err.message || DEFAULT_MESSAGE + res.status(status).json({ message }) + } +} + +module.exports = () => middleware diff --git a/src/common/errors.js b/src/common/errors.js new file mode 100644 index 0000000..cafa4df --- /dev/null +++ b/src/common/errors.js @@ -0,0 +1,27 @@ +/** + * errors defined + */ + +/** + * the base error class + */ +class AppError extends Error { + constructor (status, message) { + super() + this.status = status + this.message = message || 'unknown exception' + } +} + +module.exports = { + newBadRequestError: (msg) => new AppError( + 400, + msg || 'The request could not be interpreted correctly or some required parameters were missing.' + ), + newEntityNotFoundError: msg => new AppError(404, msg || 'The entity does not exist.'), + newAuthError: msg => new AppError(401, msg || 'Auth failed.'), + newPermissionError: msg => new AppError(403, msg || 'The entity does not exist.'), + newConflictError: msg => new AppError(409, msg || 'The entity does not exist.'), + serviceUnavailableError: msg => new AppError(503, msg || 'One or more services are not available'), + elasticSearchEnrichError: msg => new AppError(500, msg || 'Elasticsearch enrich failed') +} diff --git a/src/common/es-client.js b/src/common/es-client.js new file mode 100644 index 0000000..2ffc3f1 --- /dev/null +++ b/src/common/es-client.js @@ -0,0 +1,41 @@ +const config = require('config') +const AWS = require('aws-sdk') +const elasticsearch = require('elasticsearch') + +AWS.config.region = config.AWS_REGION + +// Elasticsearch client +let esClient + +/** + * Get ES Client + * @return {Object} Elasticsearch Client Instance + */ +function getESClient () { + if (esClient) { + return esClient + } + const host = config.ES.HOST + const apiVersion = config.ES.API_VERSION + + if (!esClient) { + // AWS ES configuration is different from other providers + if (/.*amazonaws.*/.test(host)) { + esClient = new elasticsearch.Client({ + apiVersion, + host, + connectionClass: require('http-aws-es') // eslint-disable-line global-require + }) + } else { + esClient = new elasticsearch.Client({ + apiVersion, + host + }) + } + } + return esClient +} + +module.exports = { + getESClient +} diff --git a/src/common/es-helper.js b/src/common/es-helper.js new file mode 100644 index 0000000..595c4f9 --- /dev/null +++ b/src/common/es-helper.js @@ -0,0 +1,1542 @@ +const config = require('config') +const _ = require('lodash') +const querystring = require('querystring') +const logger = require('../common/logger') +const groupApi = require('./group-api') +const appConst = require('../consts') +const esClient = require('./es-client').getESClient() + +const DOCUMENTS = config.ES.DOCUMENTS +const RESOURCES = Object.keys(DOCUMENTS) + +const SUB_USER_DOCUMENTS = {} +const SUB_ORG_DOCUMENTS = {} +const SUB_USER_PROPERTIES = [] +const SUB_ORG_PROPERTIES = [] +_.forOwn(DOCUMENTS, (value, key) => { + if (value.userField) { + SUB_USER_DOCUMENTS[key] = value + SUB_USER_PROPERTIES.push(value.userField) + } else if (value.orgField) { + SUB_ORG_DOCUMENTS[key] = value + SUB_ORG_PROPERTIES.push(value.orgField) + } +}) + +const USER_ATTRIBUTE = { + esDocumentPath: 'attributes', + esDocumentQuery: 'attributes.attributeId.keyword', + esDocumentValueStringQuery: 'attributes.value', + esDocumentValueQuery: 'attributes.value.keyword', + esDocumentId: 'attributes.id' +} + +const USER_ORGANIZATION = { + esOrganizationQuery: 'externalProfiles.organizationId.keyword' +} + +const USER_FILTER_TO_MODEL = { + skill: { + name: 'skill', + isAttribute: false, + model: require('../models/Skill'), + queryField: 'name', + esDocumentQuery: 'skills.skillId.keyword', + values: [] + }, + get skills () { return this.skill }, + achievement: { + name: 'achievement', + isAttribute: false, + model: require('../models/Achievement'), + queryField: 'name', + esDocumentValueQuery: 'achievements.name', + esDocumentQuery: 'achievements.id.keyword', + esDocumentId: 'achievements.id', + values: [] + }, + get achievements () { return this.achievement }, + location: { + name: 'location', + isAttribute: true, + attributeName: 'location', + esDocumentPath: USER_ATTRIBUTE.esDocumentPath, + esDocumentQuery: USER_ATTRIBUTE.esDocumentQuery, + esDocumentValueQuery: USER_ATTRIBUTE.esDocumentValueQuery, + defaultSortOrder: 'asc', + values: [] + }, + get locations () { return this.location }, + isavailable: { + name: 'isAvailable', + isAttribute: true, + attributeName: 'isAvailable', + esDocumentPath: USER_ATTRIBUTE.esDocumentPath, + esDocumentQuery: USER_ATTRIBUTE.esDocumentQuery, + esDocumentValueQuery: USER_ATTRIBUTE.esDocumentValueQuery, + defaultSortOrder: 'desc', // results in ordering: true false + values: [] + }, + company: { + name: 'company', + isAttribute: true, + attributeName: 'company', + esDocumentPath: USER_ATTRIBUTE.esDocumentPath, + esDocumentQuery: USER_ATTRIBUTE.esDocumentQuery, + esDocumentValueQuery: USER_ATTRIBUTE.esDocumentValueQuery, + defaultSortOrder: 'asc', + values: [] + } +} + +// resource filter config +const RESOURCE_FILTER = { + // independent resources + user: { + handle: { + resource: 'user', + queryField: 'handle' + }, + roleId: { + resource: 'userrole', + queryField: 'roleId' + } + }, + role: { + name: { + resource: 'role', + queryField: 'name' + } + }, + skill: { + externalId: { + resource: 'skill', + queryField: 'externalId' + }, + skillProviderId: { + resource: 'skill', + queryField: 'skillProviderId' + }, + name: { + resource: 'skill', + queryField: 'name' + } + }, + skillprovider: { + name: { + resource: 'skillprovider', + queryField: 'name' + } + }, + achievementprovider: { + name: { + resource: 'achievementprovider', + queryField: 'name' + } + }, + organization: { + name: { + resource: 'organization', + queryField: 'name' + } + }, + attribute: { + name: { + resource: 'attribute', + queryField: 'name' + }, + attributeGroupId: { + resource: 'attribute', + queryField: 'attributeGroupId' + } + }, + attributegroup: { + name: { + resource: 'attributegroup', + queryField: 'name' + }, + organizationId: { + resource: 'attributegroup', + queryField: 'organizationId' + } + }, + // sub-resources + userskill: { + skillName: { + resource: 'skill', + queryField: 'name' + } + }, + externalprofile: { + organizationName: { + resource: 'organization', + queryField: 'name' + }, + externalId: { + resource: 'externalprofile', + queryField: 'externalId' + }, + isInactive: { + resource: 'externalprofile', + queryField: 'isInactive' + } + }, + achievement: { + achievementsProviderName: { + resource: 'achievementprovider', + queryField: 'name' + } + }, + userattribute: { + attributeName: { + resource: 'attribute', + queryField: 'name' + }, + attributeGroupName: { + resource: 'attributegroup', + queryField: 'name' + }, + attributeGroupId: { + resource: 'attributegroup', + queryField: 'id' + } + } +} + +// filter chain config +const FILTER_CHAIN = { + user: { + idField: 'id' + }, + skillprovider: { + filterNext: 'skill', + queryField: 'skillProviderId', + idField: 'id' + }, + skill: { + filterNext: 'userskill', + queryField: 'skillId', + enrichNext: 'skillprovider', + idField: 'skillProviderId' + }, + attribute: { + filterNext: 'userattribute', + queryField: 'attributeId', + enrichNext: 'attributegroup', + idField: 'attributeGroupId' + }, + attributegroup: { + filterNext: 'attribute', + queryField: 'attributeGroupId', + idField: 'id' + }, + role: { + filterNext: 'userrole', + queryField: 'roleId', + idField: 'id' + }, + achievementprovider: { + filterNext: 'achievement', + queryField: 'achievementsProviderId', + idField: 'id' + }, + organization: { + filterNext: 'externalprofile', + queryField: 'organizationId', + idField: 'id' + }, + // sub resource + userskill: { + queryField: 'skillId', + enrichNext: 'skill', + idField: 'skillId' + }, + userrole: { + queryField: 'roleId', + enrichNext: 'role', + idField: 'roleId' + }, + externalprofile: { + enrichNext: 'organization', + idField: 'organizationId' + }, + achievement: { + enrichNext: 'achievementprovider', + idField: 'achievementsProviderId' + }, + userattribute: { + enrichNext: 'attribute', + idField: 'attributeId' + }, + organizationskillprovider: { + queryField: 'skillProviderId', + enrichNext: 'skillprovider', + idField: 'skillProviderId' + } +} + +function getTotalCount (total) { + return typeof total === 'number' ? total : total.value +} + +function escapeRegex (str) { + /* eslint-disable no-useless-escape */ + return str + .replace(/[\*\+\-=~><\"\?^\${}\(\)\:\!\/[\]\\\s]/g, '\\$&') // replace single character special characters + .replace(/\|\|/g, '\\||') // replace || + .replace(/\&\&/g, '\\&&') // replace && + .replace(/AND/g, '\\A\\N\\D') // replace AND + .replace(/OR/g, '\\O\\R') // replace OR + .replace(/NOT/g, '\\N\\O\\T') // replace NOT + /* eslint-enable no-useless-escape */ +} + +async function getOrganizationId (handle) { + const DBHelper = require('../models/index').DBHelper + + // TODO Use the service method instead of raw query + const orgIdLookupResults = await DBHelper.find(require('../models/ExternalProfile'), [ + 'select * from DUser, ExternalProfile', + `DUser.handle='${handle}'`, + 'DUser.id = ExternalProfile.userId' + ]) + + if (orgIdLookupResults.length > 0) { + throw new Error(`Handle ${handle} is associated with multiple organizations. Cannot select one.`) + } else if (orgIdLookupResults.length === 1) { + return orgIdLookupResults[0].organizationId + } + + throw new Error('User doesn\'t belong to any organization') +} + +async function getAttributeId (organizationId, attributeName) { + const DBHelper = require('../models/index').DBHelper + + // TODO Use the service method instead of raw query + const attributeIdLookupResults = await DBHelper.find(require('../models/Attribute'), [ + 'select Attribute.id from AttributeGroup, Attribute', + `AttributeGroup.organizationId = '${organizationId}'`, + 'Attribute.attributeGroupId = AttributeGroup.id', + `Attribute.name = '${attributeName}'` + ]) + + if (attributeIdLookupResults.length > 0) { + return attributeIdLookupResults[0].id + } + + throw new Error(`Attribute ${attributeName} is invalid for the current users organization`) +} + +function parseUserFilter (params) { + const filters = {} + _.forOwn(params, (value, key) => { + key = key.toLowerCase() + if (USER_FILTER_TO_MODEL[key]) { + if (!filters[key]) { + filters[key] = _.clone(USER_FILTER_TO_MODEL[key]) + } + + filters[key].values = _.isString(value) ? [value] : value + } + }) + + return filters +} + +/** + * Parse the query parameters to resource filters for enrich. + * @param params the request query parameters + * resources which are not in user index. + * @returns {{}} the resource filters keyed by resource + */ +function parseEnrichFilter (params) { + const filters = {} + _.forOwn(params, (value, key) => { + if (key.includes('.')) { + const tokens = key.split('.') + const resource = tokens[0].toLowerCase() + // only valid resource filter + if (RESOURCES.indexOf(resource)) { + const doc = DOCUMENTS[resource] + if (!filters[resource]) { + filters[resource] = [] + } + filters[resource].push({ + resource, + // the property to apply a filter + queryField: tokens[1], + // the property in user index if resource is top-level + userField: doc.userField, + // the value to match + value: value + }) + } + } + }) + return filters +} + +/** + * Enrich a resource recursively by following enrich path. + * @param resource the resource to enrich + * @param enrichIdProp the id property of child resource in parent object + * @param item the parent object + * @returns {Promise} the promise of enriched parent object + */ +async function enrichResource (resource, enrichIdProp, item) { + const subDoc = DOCUMENTS[resource] + const filterChain = FILTER_CHAIN[resource] + const subResult = await esClient.getSource({ + index: subDoc.index, + type: subDoc.type, + id: item[enrichIdProp] + }) + + if (filterChain.enrichNext) { + const enrichIdProp = filterChain.idField + // return error if any id is missing in enrich path + if (!subResult[enrichIdProp]) { + throw new Error(`The parent ${resource} is missing id value of child resource ${filterChain.enrichNext}`) + } + // enrich next child resource recursively + await enrichResource(filterChain.enrichNext, enrichIdProp, subResult) + } + item[resource] = subResult +} + +/** + * Enrich a user. + * @param user the user object to enrich + * @returns {Promise<*>} the promise of enriched user + */ +async function enrichUser (user) { + for (const subProp of Object.keys(SUB_USER_DOCUMENTS)) { + const subDoc = SUB_USER_DOCUMENTS[subProp] + const subData = user[subDoc.userField] + const filterChain = FILTER_CHAIN[subProp] + if (subData && subData.length > 0) { + // enrich next level sub resources + for (const subItem of subData) { + await enrichResource(filterChain.enrichNext, filterChain.idField, subItem) + } + } + } + return user +} + +/** + * Enrich users. + * @param users list of users from ES search + * @returns {Promise<*>} the enriched users + */ +async function enrichUsers (users) { + const enrichedUsers = [] + for (const user of users) { + const enriched = await enrichUser(user) + enrichedUsers.push(enriched) + } + return enrichedUsers +} + +/** + * Get a resource by Id from ES. + * @param resource the resource to get + * @param args the request path and query parameters + * @returns {Promise<*>} the promise of retrieved resource object from ES + */ +async function getFromElasticSearch (resource, ...args) { + logger.debug(`Get from ES first: args ${JSON.stringify(args, null, 2)}`) + const id = args[0] + // path and query parameters + const params = args[2] + + const doc = DOCUMENTS[resource] + const userDoc = DOCUMENTS.user + const orgDoc = DOCUMENTS.organization + const subUserDoc = SUB_USER_DOCUMENTS[resource] + const subOrgDoc = SUB_ORG_DOCUMENTS[resource] + const filterChain = FILTER_CHAIN[resource] + + let esQuery + + // construct ES query + if (doc.userField) { + esQuery = { + index: userDoc.index, + type: userDoc.type, + id: params.userId + } + } else if (doc.orgField) { + esQuery = { + index: orgDoc.index, + type: orgDoc.type, + id: params.organizationId + } + } else { + esQuery = { + index: doc.index, + type: doc.type, + id: id + } + } + + if (resource === 'user') { + // handle enrich + if (!params.enrich) { + esQuery._source_excludes = SUB_USER_PROPERTIES.join(',') + } + } else if (subUserDoc) { + esQuery._source_includes = subUserDoc.userField + } else if (subOrgDoc) { + esQuery._source_includes = subOrgDoc.orgField + } + + logger.debug(`ES query for get ${resource}: ${JSON.stringify(esQuery, null, 2)}`) + + // query ES + const result = await esClient.getSource(esQuery) + + if (params.enrich && resource === 'user') { + const user = await enrichUser(result) + const groups = await groupApi.getGroups(user.id) + user.groups = groups + return user + } else if (subUserDoc) { + // find top sub doc by sub.id + const found = result[subUserDoc.userField].find(sub => sub[filterChain.idField] === params[filterChain.idField]) + if (found) { + return found + } else { + throw new Error(`${resource} of userId ${params.userId}, ${params[filterChain.idField]} is not found from ES`) + } + } else if (subOrgDoc) { + // find top sub doc by sub.id + const found = result[subOrgDoc.orgField].find(sub => sub[filterChain.idField] === params[filterChain.idField]) + if (found) { + return found + } else { + throw new Error(`${resource} of organizationId ${params.organizationId}, ${params[filterChain.idField]} is not found from ES`) + } + } + return result +} + +/** + * Parse the query parameters to resource filter list. + * @param resource the resource to apply filter + * @param params the request query parameter + * @param itself true mean the filter is applied the resource itself + * @returns {[]} parsed filter array + */ +function parseResourceFilter (resource, params, itself) { + const resDefinedFilters = RESOURCE_FILTER[resource] + const resFilters = [] + if (resDefinedFilters) { + const resQueryParams = _.pick(params, Object.keys(resDefinedFilters)) + if (!_.isEmpty(resQueryParams)) { + // filter by child resource + _.forOwn(resQueryParams, (value, query) => { + const filterDef = resDefinedFilters[query] + if (itself && resource === filterDef.resource) { + resFilters.push({ + param: query, + resource: filterDef.resource, + queryField: filterDef.queryField, + value + }) + } else if (!itself && resource !== filterDef.resource) { + resFilters.push({ + param: query, + resource: filterDef.resource, + queryField: filterDef.queryField, + value + }) + } + }) + } + } + return resFilters +} + +/** + * Set the filters to ES query. + * @param resFilters the resource filters + * @param esQuery the ES query + */ +function setResourceFilterToEsQuery (resFilters, esQuery) { + if (resFilters.length > 0) { + for (const filter of resFilters) { + const doc = DOCUMENTS[filter.resource] + let matchField + if (doc.userField) { + matchField = `${doc.userField}.${filter.queryField}` + } else if (doc.orgField) { + matchField = `${doc.orgField}.${filter.queryField}` + } else { + matchField = `${filter.queryField}` + } + if (filter.queryField !== 'name' && filter.queryField !== 'isInactive') { + matchField = matchField + '.keyword' + } + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + } +} + +/** + * Set filter values to ES query. + * @param esQuery the ES query object + * @param matchField the field to match + * @param filterValue the filter value, it can be array or single value + * @param queryField the field that the filter applies + * @returns {*} the ES query + */ +function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) { + if (queryField !== 'name' && queryField !== 'isInactive') { + matchField = matchField + '.keyword' + } + if (Array.isArray(filterValue)) { + for (const value of filterValue) { + esQuery.body.query.bool.should.push({ + match: { + [matchField]: value + } + }) + } + esQuery.body.query.bool.minimum_should_match = 1 + } else { + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filterValue + } + }) + } + return esQuery +} + +/** + * Set attribute filters to an ES query to filter users by attributes + * + * @param filterClause the filter clause of the ES query + * @param attributes array of attribute id and value objects + */ +function setUserAttributesFiltersToEsQuery (filterClause, attributes) { + for (const attribute of attributes) { + if (typeof attribute.value !== 'object') { + attribute.value = [attribute.value] + } + + filterClause.push({ + nested: { + path: USER_ATTRIBUTE.esDocumentPath, + query: { + bool: { + filter: [{ + term: { + [USER_ATTRIBUTE.esDocumentQuery]: attribute.id + } + }], + should: attribute.value.map(val => { + return { + query_string: { + default_field: `${[USER_ATTRIBUTE.esDocumentValueQuery]}`, + query: `*${val.replace(/ +/g, ' ').split(' ').map(p => escapeRegex(p)).join('* AND *')}*` + } + } + }), + minimum_should_match: 1 + } + } + } + }) + } +} + +function setUserOrganizationFiilterToEsQuery (filterClause, organizationId) { + filterClause.push({ + term: { + [USER_ORGANIZATION.esOrganizationQuery]: organizationId + } + }) +} + +function hasNonAlphaNumeric (text) { + const regex = /^[A-Za-z0-9 ]+$/ + return !regex.test(text) +} + +/** + * Get skillIds matching the search keyword + * + * @param keyword the search keyword + * @returns array of skillIds + */ +async function searchSkills (keyword, skillProviderIds) { + const queryDoc = DOCUMENTS.skill + keyword = escapeRegex(keyword) + const query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` + + const keywordSearchClause = { + query_string: { + default_field: 'name', + minimum_should_match: '100%', + query + } + } + + const searchClause = { + query: {} + } + + if (skillProviderIds == null) { + searchClause.query = keywordSearchClause + searchClause._source = 'id' + } else { + searchClause.query = { + bool: { + filter: [{ + terms: { + [`${RESOURCE_FILTER.skill.skillProviderId.queryField}.keyword`]: skillProviderIds + } + }], + must: [keywordSearchClause] + } + } + } + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: searchClause + } + + logger.debug(`ES query for searching skills: ${JSON.stringify(esQuery, null, 2)}`) + const results = await esClient.search(esQuery) + + return results.hits.hits.map(hit => hit._source) +} + +async function setUserSearchClausesToEsQuery (boolClause, keyword) { + const skillIds = (await searchSkills(keyword)).map(skill => skill.id) + boolClause.should.push({ + query_string: { + fields: ['firstName', 'lastName', 'handle'], + query: `\\*${escapeRegex(keyword.replace(/ +/g, ' ')).split(' ').join('\\* OR \\*')}\\*` + } + }) + + keyword = escapeRegex(keyword) + boolClause.should.push({ + nested: { + path: USER_ATTRIBUTE.esDocumentPath, + query: { + query_string: { + query: hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*`, + fields: [USER_ATTRIBUTE.esDocumentValueStringQuery] + } + } + } + }, { + query_string: { + query: hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*`, + fields: [USER_FILTER_TO_MODEL.achievement.esDocumentValueQuery] + } + }) + + if (skillIds.length > 0) { + boolClause.should.push({ + terms: { + [USER_FILTER_TO_MODEL.skill.esDocumentQuery]: skillIds + } + }) + } + + boolClause.minimum_should_match = 1 +} + +/** + * Build ES query from given filter. + * @param filter + * @returns {{}} created ES query object + */ +function buildEsQueryFromFilter (filter) { + const queryDoc = DOCUMENTS[filter.resource] + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + query: { + bool: { + must: [], + should: [], + minimum_should_match: 0 + } + } + } + } + + const matchField = `${filter.queryField}` + return setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) +} + +/** + * Returns if char is one of the reserved regex characters + * @param {*} char the char to check + */ +function isRegexReserved (char) { + const reserved = '^$#@&<>~.?+*|{}[]()"\\' + return reserved.indexOf(char) !== -1 +} + +function buildEsQueryToGetAchievements (organizationId, keyword, size) { + const queryDoc = DOCUMENTS.user + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + size: 0, + query: { + bool: { + filter: [] + } + }, + aggs: { + achievements: { + terms: { + field: `${USER_FILTER_TO_MODEL.achievement.esDocumentValueQuery}.keyword`, + include: `.*${keyword.replace(/[^a-zA-Z]/g, c => `[${!isRegexReserved(c) ? c : '\\' + c}]`).replace(/[A-Za-z]/g, c => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, + size: size || 1000 + }, + aggs: { + ids: { + top_hits: { + _source: [USER_FILTER_TO_MODEL.achievement.esDocumentId, USER_FILTER_TO_MODEL.achievement.esDocumentValueQuery] + } + } + } + } + } + } + } + + setUserOrganizationFiilterToEsQuery(esQuery.body.query.bool.filter, organizationId) + + return esQuery +} + +/** + * Build ES Query to get attribute values by attributeId + * @param attributeId the attribute whose values to fetch + * @param attributeValue only fetch values that are case insensitive substrings of attributeValue + * @param size the number of attributes to fetch + * @return {{}} created ES query object + */ +function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) { + const queryDoc = DOCUMENTS.user + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + size: 0, + aggs: { + attributes: { + nested: { + path: USER_ATTRIBUTE.esDocumentPath + }, + aggs: { + ids: { + terms: { + field: USER_ATTRIBUTE.esDocumentQuery, + include: attributeId + }, + aggs: { + values: { + terms: { + field: USER_ATTRIBUTE.esDocumentValueQuery, + include: `.*${attributeValue.replace(/[^a-zA-Z]/g, c => `[${!isRegexReserved(c) ? c : '\\' + c}]`).replace(/[A-Za-z]/g, c => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, + order: { + _key: 'asc' + }, + size: size || 1000 + }, + aggs: { + attribute: { + top_hits: { + size: 1, + _source: USER_ATTRIBUTE.esDocumentId + } + } + } + } + } + } + } + } + } + } + } + + return esQuery +} + +function buildEsQueryToGetSkillProviderIds (organizationId) { + const queryDoc = DOCUMENTS.organization + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + size: 1000, + query: { + term: { + 'id.keyword': { + value: organizationId + } + } + } + } + } + + return esQuery +} + +async function resolveUserFilterFromDb (filter, { handle }, organizationId) { + const DBHelper = require('../models/index').DBHelper + + if (filter.isAttribute) { + const esQueryClause = { + bool: { + filter: [], + should: [], + minimum_should_match: 0 + } + } + + if (organizationId == null) { + organizationId = await getOrganizationId(handle) + } + + const attributeId = await getAttributeId(organizationId, filter.attributeName) + + esQueryClause.bool.filter.push({ + term: { + [filter.esDocumentQuery]: attributeId + } + }) + + if (typeof filter.values !== 'object') { + filter.values = [filter.values] + } + + for (const value of filter.values) { + if (value === 'true' || value === 'false') { + esQueryClause.bool.should.push({ + term: { + [filter.esDocumentValueQuery]: value + } + }) + } else { + esQueryClause.bool.should.push({ + query_string: { + default_field: `${filter.esDocumentValueQuery}`, + query: `*${value.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + } + }) + } + } + + esQueryClause.bool.minimum_should_match = 1 + + return { + nested: { + path: filter.esDocumentPath, + query: esQueryClause + } + } + } else { + const esQueryClause = { + bool: { + should: [], + minimum_should_match: 0 + } + } + + const model = filter.model + + let filterValuesQuery = `${filter.queryField} like '%${filter.values[0]}%'` + const nFilterValues = filter.values.length + for (let i = 1; i < nFilterValues; i++) { + filterValuesQuery = `${filterValuesQuery} OR ${filter.queryField} like '%${filter.values[i]}%'` + } + + // TODO Use the service method instead of raw query + const dbQueries = [ + filterValuesQuery + ] + + const results = await DBHelper.find(model, dbQueries) + if (results.length > 0) { + for (const { id } of results) { + esQueryClause.bool.should.push({ + term: { + [filter.esDocumentQuery]: id + } + }) + } + esQueryClause.bool.minimum_should_match = 1 + return esQueryClause + } else { + throw new Error(`Lookup data of type ${filter.name} with one or more value(s) in (${filter.values.join(', ')}) does not exist. Cannot filter records using it as reference`) + } + } +} + +async function resolveSortClauseFromDb (orderBy, { handle }, organizationId) { + if (orderBy === 'name') { + return [{ + 'firstName.keyword': 'asc' + }, { + 'lastName.keyword': 'asc' + }] + } + + const attributes = _.filter(USER_FILTER_TO_MODEL, d => d.isAttribute) + for (const attribute of attributes) { + if (orderBy === attribute.name) { + if (organizationId == null) organizationId = await getOrganizationId(handle) + + return [{ + [attribute.esDocumentValueQuery]: { + order: attribute.defaultSortOrder || 'asc', + nested: { + path: attribute.esDocumentPath, + filter: { + term: { + [`${attribute.esDocumentQuery}`]: await getAttributeId(organizationId, attribute.attributeName) + } + } + } + } + }] + } + } + + throw new Error(`Invalid orderBy clause encountered ${orderBy}.`) +} + +/** + * Resolve filter by querying ES with filter data. + * @param filter the filter to query ES + * @param initialRes the resource that initial request comes in + * @returns {Promise<*>} the resolved value + */ +async function resolveResFilter (filter, initialRes) { + const doc = DOCUMENTS[filter.resource] + const filterChain = FILTER_CHAIN[filter.resource] + + // return the value if this is end of the filter + if (filter.resource === initialRes || !filterChain.filterNext) { + if (doc.orgField) { + return { + resource: filter.resource, + orgField: doc.orgField, + queryField: filter.queryField, + value: filter.value + } + } else { + return { + resource: filter.resource, + userField: doc.userField, + queryField: filter.queryField, + value: filter.value + } + } + } + + // query ES with filter + const esQuery = buildEsQueryFromFilter(filter) + const result = await esClient.search(esQuery) + + const numHits = getTotalCount(result.hits.total) + + if (numHits > 0) { + // this value can be array + let value + if (numHits === 1) { + value = result.hits.hits[0]._source.id + } else { + value = result.hits.hits.map(hit => hit._source.id) + } + const nextFilter = { + resource: filterChain.filterNext, + queryField: filterChain.queryField, + value + } + // go to next filter + const resolved = await resolveResFilter(nextFilter, initialRes) + return resolved + } + throw new Error(`Resource filter[${filter.resource}.${filter.queryField}=${filter.value}] query returns no data`) +} + +/** + * Filter sub-resource results by query filters. + * @param results the ES query results from user index + * @param preResFilterResults the resolved filter results that applied related resources + * @param ownResFilters the filters that will be applied to the sub-resource that is requested + * @param perPage + * @returns {*} + */ +function applySubResFilters (results, preResFilterResults, ownResFilters, perPage) { + let count = 0 + const filtered = results.filter(item => { + for (const filter of preResFilterResults) { + if (item[filter.queryField] !== filter.value) { + return false + } + } + for (const filter of ownResFilters) { + if (item[filter.queryField] !== filter.value) { + return false + } + } + count += 1 + if (count > perPage) { + return false + } + return true + }) + return filtered +} + +/** + * Search ES with provided request parameters. + * @param resource the resource to search + * @param args the request path and query parameters + * @returns {Promise<*>} the promise of searched results + */ +async function searchElasticSearch (resource, ...args) { + const { + checkIfExists, + getAuthUser + } = require('./helper') + logger.debug(`Searching ES first: ${JSON.stringify(args, null, 2)}`) + // path and query parameters + const params = args[0] + const authUser = args[1] + const doc = DOCUMENTS[resource] + const userDoc = DOCUMENTS.user + const orgDoc = DOCUMENTS.organization + const topUserSubDoc = SUB_USER_DOCUMENTS[resource] + const topOrgSubDoc = SUB_ORG_DOCUMENTS[resource] + if (!params.page) { + params.page = 1 + } + if (!params.perPage) { + params.perPage = config.PAGE_SIZE + } + + let sortClause = [] + + const preResFilters = parseResourceFilter(resource, params, false) + const preResFilterResults = [] + // resolve pre resource filters + if (!params.enrich && preResFilters.length > 0) { + for (const filter of preResFilters) { + const resolved = await resolveResFilter(filter, resource) + preResFilterResults.push(resolved) + } + } + + // resolve pre-enrich filters except sub-resource filter + const enrichFilters = parseEnrichFilter(params) + const enrichFilterResults = [] + if (params.enrich && resource === 'user') { + for (const res of Object.keys(enrichFilters)) { + const enFilters = enrichFilters[res] + // filter the non sub-resource + for (const filter of enFilters) { + if (!filter.userField) { + const resolved = await resolveResFilter(filter) + enrichFilterResults.push(resolved) + } + } + } + } + + const userFilters = params.enrich && resource === 'user' ? parseUserFilter(params) : [] + const resolvedUserFilters = [] + if (params.enrich && resource === 'user') { + const filterKey = Object.keys(userFilters) + const authUserOrganizationId = params.organizationId + for (const key of filterKey) { + const resolved = await resolveUserFilterFromDb(userFilters[key], authUser, authUserOrganizationId) + resolvedUserFilters.push(resolved) + } + + if (params.orderBy) { + sortClause = sortClause.concat(await resolveSortClauseFromDb(params.orderBy, authUser, authUserOrganizationId)) + } + } + + // construct ES query + const esQuery = { + index: doc.userField ? userDoc.index : (doc.orgField ? orgDoc.index : doc.index), + type: doc.userField ? userDoc.type : (doc.orgField ? orgDoc.type : doc.type), + size: params.perPage, + from: (params.page - 1) * params.perPage, // Es Index starts from 0 + body: { + query: { + bool: { + must: [], + should: [], + minimum_should_match: 0 + } + }, + sort: sortClause + } + } + + // for non-admin, only return entities that the user created + if ( + authUser.roles && + !checkIfExists(authUser.roles, appConst.AdminUser) && + !checkIfExists(authUser.roles, [appConst.UserRoles.ubahn]) + ) { + setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') + } + + if (resource === 'user') { + if (params.enrich) { + // apply user filters + if (resolvedUserFilters.length > 0) { + esQuery.body.query.bool.filter = resolvedUserFilters + } + + // apply resolved pre-enrich filter values + if (enrichFilterResults.length > 0) { + for (const filter of enrichFilterResults) { + const matchField = `${filter.userField}.${filter.queryField}` + setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) + } + } + // set the sub-resource filters which is in user index + _.forOwn(enrichFilters, (enFilter, resource) => { + _.forEach(enFilter, filter => { + if (filter.userField) { + let matchField = `${filter.userField}.${filter.queryField}` + if (filter.queryField !== 'name') { + matchField = matchField + '.keyword' + } + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + }) + }) + } else { + // do not return sub-resources + esQuery._source_excludes = SUB_USER_PROPERTIES.join(',') + } + } else if (topUserSubDoc) { + // add userId match + const userFC = FILTER_CHAIN.user + const userIdMatchField = `${userFC.idField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [userIdMatchField]: params.userId + } + }) + esQuery._source_includes = topUserSubDoc.userField + } else if (topOrgSubDoc) { + // add organizationId match + const orgFC = FILTER_CHAIN.organization + const orgIdMatchField = `${orgFC.idField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [orgIdMatchField]: params.organizationId + } + }) + esQuery._source_includes = topOrgSubDoc.orgField + } + + // set pre res filter results + if (!params.enrich && preResFilterResults.length > 0) { + for (const filter of preResFilterResults) { + let matchField + if (filter.userField) { + matchField = `${filter.userField}.${filter.queryField}` + } else if (filter.orgField) { + matchField = `${filter.orgField}.${filter.queryField}` + } else { + matchField = `${filter.queryField}` + } + setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) + } + } + + const ownResFilters = parseResourceFilter(resource, params, true) + // set it's own res filter to the main query + if (!params.enrich && ownResFilters.length > 0) { + setResourceFilterToEsQuery(ownResFilters, esQuery) + } + + logger.debug(`ES query for search ${resource}: ${JSON.stringify(esQuery, null, 2)}`) + const docs = await esClient.search(esQuery) + if (docs.hits && getTotalCount(docs.hits.total) === 0) { + return { + total: docs.hits.total, + page: params.page, + perPage: params.perPage, + result: [] + } + } + + let result = [] + if (resource === 'user' && params.enrich) { + const users = docs.hits.hits.map(hit => hit._source) + result = await enrichUsers(users) + // enrich groups + for (const user of users) { + const groups = await groupApi.getGroups(user.id) + user.groups = groups + } + } else if (topUserSubDoc) { + result = docs.hits.hits[0]._source[topUserSubDoc.userField] + // for sub-resource query, it returns all sub-resource items in one user, + // so needs filtering and also page size + result = applySubResFilters(result, preResFilterResults, ownResFilters, params.perPage) + } else if (topOrgSubDoc) { + result = docs.hits.hits[0]._source[topOrgSubDoc.orgField] + // for sub-resource query, it returns all sub-resource items in one organization, + // so needs filtering and also page size + result = applySubResFilters(result, preResFilterResults, ownResFilters, params.perPage) + } else { + result = docs.hits.hits.map(hit => hit._source) + } + + return { + total: docs.hits.total, + page: params.page, + perPage: params.perPage, + result + } +} + +/** + * Searches for users. Returns enriched user information + * Difference between this as GET /users is that the latter uses query params to filter data + * and has different set of filters to query with. + * @param {Object} authUser The auth object + * @param {Object} filter The details of the search + * @param {Object} params The query params + */ +async function searchUsers (authUser, filter, params) { + const { checkIfExists, getAuthUser } = require('./helper') + const queryDoc = DOCUMENTS.user + + if (!filter.organizationId) { + throw new Error('Cannot search for users without organization info') + } + + if (!params.page) { + params.page = 1 + } + if (!params.perPage) { + params.perPage = config.PAGE_SIZE + } + + let sortClause = [] + + const userFilters = parseUserFilter(filter) + const resolvedUserFilters = [] + + const authUserOrganizationId = filter.organizationId + const filterKey = Object.keys(userFilters) + + for (const key of filterKey) { + const resolved = await resolveUserFilterFromDb(userFilters[key], authUser, authUserOrganizationId) + resolvedUserFilters.push(resolved) + } + + if (params.orderBy) { + sortClause = sortClause.concat(await resolveSortClauseFromDb(params.orderBy, authUser, authUserOrganizationId)) + } + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + size: params.perPage, + from: (params.page - 1) * params.perPage, + body: { + query: { + bool: { + must: [], + should: [], + filter: [], + minimum_should_match: 0 + } + }, + sort: sortClause + } + } + + // for non-admin, only return entities that the user created + if ( + authUser.roles && + !checkIfExists(authUser.roles, appConst.AdminUser) && + !checkIfExists(authUser.roles, [appConst.UserRoles.ubahn]) + ) { + setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') + } + + if (resolvedUserFilters.length > 0) { + esQuery.body.query.bool.filter = resolvedUserFilters + } + + if (filter.keyword != null) { + await setUserSearchClausesToEsQuery(esQuery.body.query.bool, filter.keyword) + } + + if (filter.attributes != null) { + setUserAttributesFiltersToEsQuery(esQuery.body.query.bool.filter, filter.attributes) + } + + if (filter.organizationId != null) { + setUserOrganizationFiilterToEsQuery(esQuery.body.query.bool.filter, filter.organizationId) + } + + // We never return inactive users + esQuery.body.query.bool.filter.push({ + term: { + 'externalProfiles.isInactive': false + } + }) + + logger.debug(`ES query for searching users: ${JSON.stringify(esQuery, null, 2)}`) + + const docs = await esClient.search(esQuery) + const users = docs.hits.hits.map(hit => hit._source) + + logger.debug('Enrich users') + + const result = await enrichUsers(users) + // enrich groups + for (const user of users) { + const groups = await groupApi.getGroups(user.id) + user.groups = groups + } + + return { + total: getTotalCount(docs.hits.total), + page: params.page, + perPage: params.perPage, + result + } +} + +/** + * Search for skills matching the given keyword and are part of the given organization + * @param {Object} param0 the organizationId and keyword + */ +async function searchSkillsInOrganization ({ organizationId, keyword }) { + if (!organizationId) { + throw Error('Cannot search for skills without organization info') + } + const esQueryToGetSkillProviders = buildEsQueryToGetSkillProviderIds(organizationId) + logger.debug(`ES query to get skill provider ids: ${JSON.stringify(esQueryToGetSkillProviders, null, 2)}`) + + const esResultOfQueryToGetSkillProviders = await esClient.search(esQueryToGetSkillProviders) + logger.debug(`ES result: ${JSON.stringify(esResultOfQueryToGetSkillProviders, null, 2)}`) + + const skillProviderIds = _.flatten(esResultOfQueryToGetSkillProviders.hits.hits.map(hit => hit._source.skillProviders == null ? [] : hit._source.skillProviders.map(sp => sp.skillProviderId))) + logger.debug(`Organization ${organizationId} yielded skillProviderIds: ${JSON.stringify(skillProviderIds, null, 2)}`) + + const skills = await searchSkills(keyword, skillProviderIds) + + return { + result: skills.map(skill => ({ + name: skill.name, + skillId: skill.id, + skillProviderId: skill.skillProviderId + // skillProviderName: 'TODO' + })) + } +} + +/** + * Searches for matching values for the given attribute value, under the given attribute id + * @param {Object} param0 The attribute id and the attribute value properties + */ +async function searchAttributeValues ({ attributeId, attributeValue }) { + const esQuery = buildEsQueryToGetAttributeValues(attributeId, querystring.unescape(attributeValue), 5) + logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) + + const esResult = await esClient.search(esQuery) + logger.debug(`ES Result: ${JSON.stringify(esResult, null, 2)}`) + const result = [] + const attributes = esResult.aggregations.attributes.ids.buckets + + for (const attribute of attributes) { + const id = attribute.key + const values = attribute.values.buckets + + for (const value of values) { + result.push({ + attributeId: id, + value: value.key, + id: value.attribute.hits.hits[0]._source.id + }) + } + } + + return { result } +} + +async function searchAchievementValues ({ organizationId, keyword }) { + if (!organizationId) { + throw Error('Cannot search for achievements without organization info') + } + const esQuery = buildEsQueryToGetAchievements(organizationId, querystring.unescape(keyword), 5) + logger.debug(`ES query for searching achievement values; ${JSON.stringify(esQuery, null, 2)}`) + + const esResult = await esClient.search(esQuery) + logger.debug(`ES response ${JSON.stringify(esResult, null, 2)}`) + const result = esResult.aggregations.achievements.buckets.map(a => { + const achievementName = a.key + let achievementId = null + + for (const achievement of a.ids.hits.hits[0]._source.achievements) { + if (achievement.name === achievementName) { + achievementId = achievement.id + break + } + } + return { + id: achievementId, + name: achievementName + } + }) + + return { + result + } +} + +module.exports = { + searchElasticSearch, + getFromElasticSearch, + searchUsers, + searchSkillsInOrganization, + searchAttributeValues, + searchAchievementValues +} diff --git a/src/common/group-api.js b/src/common/group-api.js new file mode 100644 index 0000000..9115657 --- /dev/null +++ b/src/common/group-api.js @@ -0,0 +1,37 @@ +const config = require('config') +const _ = require('lodash') +const axios = require('axios') +const m2mAuth = require('tc-core-library-js').auth.m2m +const logger = require('./logger') + +const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL'])) + +/** + * Get M2M token. + * @returns {Promise} + */ +async function getM2Mtoken () { + return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) +} + +async function getGroups (universalUID) { + const m2mToken = await getM2Mtoken() + + try { + const resp = await axios({ + method: 'get', + params: { universalUID, membershipType: 'user' }, + url: config.GROUP_API_URL, + headers: { Authorization: `Bearer ${m2mToken}` } + }) + return resp.data + } catch (error) { + logger.error(error) + return [] + } +} + +module.exports = { + getM2Mtoken, + getGroups +} diff --git a/src/common/helper.js b/src/common/helper.js new file mode 100644 index 0000000..e6bda2d --- /dev/null +++ b/src/common/helper.js @@ -0,0 +1,262 @@ +const { + Decimal, + IonTypes, + Timestamp +} = require('ion-js') + +const config = require('config') +const querystring = require('querystring') +const errors = require('./errors') +const appConst = require('../consts') +const _ = require('lodash') +const { getServiceMethods } = require('./service-helper') +const { getControllerMethods, getSubControllerMethods } = require('./controller-helper') +const logger = require('./logger') +const busApi = require('tc-bus-api-wrapper') +const busApiClient = busApi(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_CLIENT_ID', + 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) + +/** + * convert json object to ion.js writer + * @param value the json object + * @param ionWriter the writer + */ +function writeValueAsIon (value, ionWriter) { + switch (typeof value) { + case 'string': + ionWriter.writeString(value) + break + case 'boolean': + ionWriter.writeBoolean(value) + break + case 'number': + ionWriter.writeInt(value) + break + case 'object': + if (Array.isArray(value)) { + // Object is an array. + ionWriter.stepIn(IonTypes.LIST) + + for (const element of value) { + writeValueAsIon(element, ionWriter) + } + + ionWriter.stepOut() + } else if (value instanceof Date) { + // Object is a Date. + ionWriter.writeTimestamp(Timestamp.parse(value.toISOString())) + } else if (value instanceof Decimal) { + // Object is a Decimal. + ionWriter.writeDecimal(value) + } else if (value === null) { + ionWriter.writeNull(IonTypes.NULL) + } else { + // Object is a struct. + ionWriter.stepIn(IonTypes.STRUCT) + + for (const key of Object.keys(value)) { + ionWriter.writeFieldName(key) + writeValueAsIon(value[key], ionWriter) + } + ionWriter.stepOut() + } + break + default: + throw errors.newBadRequestError(`Cannot convert to Ion for type: ${(typeof value)}.`) + } +} + +/** + * Reader to json object + * @param reader the ion.js Reader + * @returns {{}} + */ +function readerToJson (reader) { + reader.next() + reader.stepIn() + + const obj = {} + + const toRealValue = (r, result) => { + let nextT = reader.next() + + const setValue = (key, value) => { + if (key) { + result[key] = value + } else { + result.push(value) + } + return value + } + + while (nextT) { + const name = r.fieldName() + switch (nextT) { + case IonTypes.STRING: + setValue(name, r.stringValue()) + break + case IonTypes.TIMESTAMP: + setValue(name, r.timestampValue().getDate()) + break + case IonTypes.NULL: + setValue(name, null) + break + case IonTypes.INT: + setValue(name, r.numberValue()) + break + case IonTypes.LIST: + r.stepIn() + toRealValue(r, setValue(name, [])) + r.stepOut() + break + } + nextT = reader.next() + } + } + + toRealValue(reader, obj) + return obj +} + +/** + * get auth user handle or id + * @param authUser the user + */ +function getAuthUser (authUser) { + return authUser.handle || authUser.sub +} + +/** + * Checks if the source matches the term. + * + * @param {Array} source the array in which to search for the term + * @param {Array | String} term the term to search + */ +function checkIfExists (source, term) { + let terms + + if (!_.isArray(source)) { + throw new Error('Source argument should be an array') + } + + source = source.map(s => s.toLowerCase()) + + if (_.isString(term)) { + terms = term.split(' ') + } else if (_.isArray(term)) { + terms = term.map(t => t.toLowerCase()) + } else { + throw new Error('Term argument should be either a string or an array') + } + + for (let i = 0; i < terms.length; i++) { + if (source.includes(terms[i])) { + return true + } + } + + return false +} + +/** + * Get link for a given page. + * @param {Object} req the HTTP request + * @param {Number} page the page number + * @returns {String} link for the page + */ +function getPageLink (req, page) { + const q = _.assignIn({}, req.query, { page }) + return `${req.protocol}://${req.get('Host')}${req.baseUrl}${req.path}?${querystring.stringify(q)}` +} + +/** + * Set HTTP response headers from result. + * @param {Object} req the HTTP request + * @param {Object} res the HTTP response + * @param {Object} result the operation result + */ +function injectSearchMeta (req, res, result) { + // if result is got from db, then do not set response headers + if (result.fromDB) { + return + } + + const resultTotal = _.isNumber(result.total) ? result.total : result.total.value + + const totalPages = Math.ceil(resultTotal / result.perPage) + if (result.page > 1) { + res.set('X-Prev-Page', +result.page - 1) + } + if (result.page < totalPages) { + res.set('X-Next-Page', +result.page + 1) + } + res.set('X-Page', result.page) + res.set('X-Per-Page', result.perPage) + res.set('X-Total', resultTotal) + res.set('X-Total-Pages', totalPages) + // set Link header + if (totalPages > 0) { + let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink(req, totalPages)}>; rel="last"` + if (result.page > 1) { + link += `, <${getPageLink(req, result.page - 1)}>; rel="prev"` + } + if (result.page < totalPages) { + link += `, <${getPageLink(req, result.page + 1)}>; rel="next"` + } + res.set('Link', link) + } + + // Allow browsers access pagination data in headers + let accessControlExposeHeaders = res.get('Access-Control-Expose-Headers') || '' + accessControlExposeHeaders += accessControlExposeHeaders ? ', ' : '' + // append new values, to not override values set by someone else + accessControlExposeHeaders += 'X-Page, X-Per-Page, X-Total, X-Total-Pages, X-Prev-Page, X-Next-Page' + + res.set('Access-Control-Expose-Headers', accessControlExposeHeaders) +} + +/** + * some user can only access devices that they created. admin role user can access all devices + * @param auth the auth object + * @param recordObj the record object + */ +function permissionCheck (auth, recordObj) { + if ( + auth && + auth.roles && + !checkIfExists(auth.roles, appConst.AdminUser) && + !checkIfExists(auth.roles, [appConst.UserRoles.ubahn]) && + recordObj.createdBy !== getAuthUser(auth)) { + throw errors.newPermissionError('You are not allowed to perform this action') + } +} + +/** + * Send Kafka event message + * @params {String} topic the topic name + * @params {Object} payload the payload + */ +async function postEvent (topic, payload) { + logger.debug(`Posting event to Kafka topic ${topic}, ${JSON.stringify(payload, null, 2)}`) + const message = { + topic, + originator: config.KAFKA_MESSAGE_ORIGINATOR, + timestamp: new Date().toISOString(), + 'mime-type': 'application/json', + payload + } + await busApiClient.postEvent(message) +} + +module.exports = { + writeValueAsIon, + readerToJson, + getAuthUser, + permissionCheck, + checkIfExists, + injectSearchMeta, + getControllerMethods, + getSubControllerMethods, + getServiceMethods, + postEvent +} diff --git a/src/common/logger.js b/src/common/logger.js new file mode 100755 index 0000000..953f1c5 --- /dev/null +++ b/src/common/logger.js @@ -0,0 +1,152 @@ +/** + * This module contains the winston logger configuration. + */ +/* eslint-disable no-param-reassign, func-names */ + +const _ = require('lodash') +const winston = require('winston') +const util = require('util') +const config = require('config') +const getParams = require('get-parameter-names') +const joi = require('@hapi/joi') + +const { + combine, timestamp, colorize, align, printf +} = winston.format + +const basicFormat = printf(info => `${info.timestamp} ${info.level}: ${info.message}`) + +const transports = [] +if (!config.DISABLE_LOGGING) { + transports.push(new (winston.transports.Console)({ level: config.LOG_LEVEL })) +} + +const logger = winston.createLogger({ + transports, + format: combine( + colorize(), + align(), + timestamp(), + basicFormat + ) +}) + +/** + * Log error details with signature + * @param err the error + */ +logger.logFullError = function (err) { + if (!err) { + return + } + logger.error((err.signature ? (`${err.signature} : `) : '') + util.inspect(err)) + err.logged = true +} + +/** + * Remove invalid properties from the object and hide long arrays + * @param {Object} obj the object + * @returns {Object} the new object with removed properties + * @private + */ +function sanitizeObject (obj) { + const hideFields = ['auth'] + try { + return JSON.parse(JSON.stringify(obj, (k, v) => { + return _.includes(hideFields, k) ? '' : v + })) + } catch (e) { + return obj + } +} + +/** + * Convert array with arguments to object + * @param {Array} params the name of parameters + * @param {Array} arr the array with values + * @private + */ +function combineObject (params, arr) { + const ret = {} + _.each(arr, (arg, i) => { + ret[params[i]] = arg + }) + return ret +} + +/** + * Decorate all functions of a service and log debug information if DEBUG is enabled + * @param {string} serviceName the service name + * @param {Object} service the service + */ +logger.decorateWithLogging = (serviceName, service) => { + if (config.LOG_LEVEL !== 'debug') { + return + } + _.each(service, (method, name) => { + const params = method.params || getParams(method) + + service[name] = async (...args) => { + if (name.indexOf('export') === 0) { + return method(...args) + } + logger.debug(`ENTER Method '${serviceName}.${name}'`) + logger.debug(`##input arguments, ${util.inspect(sanitizeObject(combineObject(params, args)))}`) + try { + const result = await method(...args) + if (result !== null && result !== undefined) { + logger.debug(`##output arguments, ${util.inspect(sanitizeObject(result))}`) + } else { + logger.debug('##output arguments, No any result returned') + } + logger.debug(`EXIT Method '${serviceName}.${name}'`) + return result + } catch (e) { + e.signature = `${serviceName}.${name}` + throw e + } + } + }) +} + +/** + * Decorate all functions of a service and validate input values + * and replace input arguments with sanitized result form Joi + * Service method must have a `schema` property with Joi schema + * @param {string} serviceName the service name + * @param {Object} service the service + */ +logger.decorateWithValidators = (serviceName, service) => { + _.each(service, (method, name) => { + if (!method.schema) { + return + } + + const params = getParams(method) + service[name] = async function (...args) { + const value = combineObject(params, args) + const normalized = (method.schema) ? await joi.object(method.schema).validateAsync(value) : value + const newArgs = [] + // Joi will normalize values + // for example string number '1' to 1 + // if schema type is number + _.each(params, (param) => { + newArgs.push(normalized[param]) + }) + return method(...newArgs) + } + service[name].params = params + }) +} + +/** + * Apply logger and validation decorators + * @param {string} serviceName the service name + * @param {Object} service the service to wrap + */ +logger.buildService = (serviceName, service) => { + logger.decorateWithValidators(serviceName, service) + logger.decorateWithLogging(serviceName, service) +} + +module.exports = logger diff --git a/src/common/service-helper.js b/src/common/service-helper.js new file mode 100644 index 0000000..c51804b --- /dev/null +++ b/src/common/service-helper.js @@ -0,0 +1,324 @@ +const joi = require('@hapi/joi') +const config = require('config') +const appConst = require('../consts') +const _ = require('lodash') +const errors = require('./errors') +const esHelper = require('./es-helper') +const logger = require('./logger') + +// mapping operation to topic +const OP_TO_TOPIC = { + create: config.UBAHN_CREATE_TOPIC, + patch: config.UBAHN_UPDATE_TOPIC, + remove: config.UBAHN_DELETE_TOPIC +} + +// Used for determining the payload structure when posting to bus api +const SUB_USER_DOCUMENTS = {} +const SUB_ORG_DOCUMENTS = {} +_.forOwn(config.ES.DOCUMENTS, (value, key) => { + if (value.userField) { + SUB_USER_DOCUMENTS[key] = value + } else if (value.orgField) { + SUB_ORG_DOCUMENTS[key] = value + } +}) + +// map model name to bus message resource if different +const MODEL_TO_RESOURCE = { + UsersSkill: 'userskill', + SkillsProvider: 'skillprovider', + AchievementsProvider: 'achievementprovider', + UsersAttribute: 'userattribute', + UsersRole: 'userrole', + OrganizationSkillsProvider: 'organizationskillprovider' +} + +/** + * Get the resource from model name + * @param modelName the model name + * @returns {string|*} the resource + */ +function getResource (modelName) { + if (MODEL_TO_RESOURCE[modelName]) { + return MODEL_TO_RESOURCE[modelName] + } else { + return modelName.toLowerCase() + } +} + +/** + * make sure reference item exists + * @param entity the request entity + */ +async function makeSureRefExist (entity) { + const models = require('../models/index') + const modelMap = { + skillProviderId: models.SkillsProvider, + roleId: models.Role, + userId: models.User, + organizationId: models.Organization, + achievementsProviderId: models.AchievementsProvider, + attributeGroupId: models.AttributeGroup, + attributeId: models.Attribute + + } + const keys = Object.keys(entity) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (modelMap[key]) { + await models.DBHelper.get(modelMap[key], entity[key], ['id']) + } + } +} + +/** + * make sure unique + * @param model the db model + * @param entity the entity + * @param uniqueFields the uniqueFields + */ +async function makeSureUnique (model, entity, uniqueFields) { + if (_.isNil(uniqueFields) || _.isEmpty(uniqueFields)) { + return + } + const models = require('../models/index') + for (let i = 0; i < uniqueFields.length; i++) { + const params = {} + _.each(uniqueFields[i], f => { + if (entity[f]) { + params[f] = entity[f] + } + }) + const items = await models.DBHelper.find(model, buildQueryByParams(params)) + if (items.length <= 0) { + continue + } + + if ((items.length > 0 && !entity.id) || + (items[0].id !== entity.id) + ) { + throw errors.newConflictError(`${model.tableName} already exists with ${_.map(params, (v, k) => `${k}:${v}`).join(', ')}`) + } + } +} + +/** + * build db query by params + * @param params + * @return {[]} + */ +function buildQueryByParams (params) { + const dbQueries = [] + _.each(params, (v, k) => { + dbQueries.push(`${k} = '${v}'`) + }) + return dbQueries +} + +/** + * Posts the message to bus api + * @param {String} op The action + * @param {String} resource The name of the resource + * @param {Object} result The payload + */ +async function publishMessage (op, resource, result) { + if (!OP_TO_TOPIC[op]) { + logger.warn(`Invalid operation: ${op}`) + return + } + + const { postEvent } = require('./helper') + + logger.debug(`Publishing message to bus: resource ${resource}, data ${JSON.stringify(result, null, 2)}`) + + // Send Kafka message using bus api + await postEvent(OP_TO_TOPIC[op], _.assign({ resource }, result)) +} + +/** + * get service methods + * @param Model the model + * @param createSchema the create joi schema + * @param patchSchema the patch joi schema + * @param searchSchema the search joi schema + * @param buildDBQuery the async build db query function + * @param uniqueFields the unique fields + * @return {any} methods + */ +function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buildDBQuery, uniqueFields) { + const models = require('../models/index') + const resource = getResource(Model.name) + const { permissionCheck, checkIfExists, getAuthUser } = require('./helper') + + /** + * create entity + * @param entity the request device entity + * @param auth the auth information + * @return {Promise} the created device + */ + async function create (entity, auth) { + await makeSureUnique(Model, entity, uniqueFields) + await makeSureRefExist(entity) + + const dbEntity = new Model() + _.extend(dbEntity, entity) + dbEntity.created = new Date() + dbEntity.createdBy = getAuthUser(auth) + await models.DBHelper.save(Model, dbEntity) + try { + await publishMessage('create', resource, dbEntity) + } catch (err) { + logger.logFullError(err) + } + return dbEntity + } + + create.schema = { + entity: createSchema, + auth: joi.object() + } + + /** + * patch device by id + * @param id the device id + * @param entity the request device entity + * @param auth the auth object + * @param params the query params + * @return {Promise} the updated device + */ + async function patch (id, entity, auth, params) { + await makeSureRefExist(entity) + + const dbEntity = await get(id, auth, params) + const newEntity = new Model() + _.extend(newEntity, dbEntity, entity) + newEntity.updated = new Date() + newEntity.updatedBy = getAuthUser(auth) + await makeSureUnique(Model, newEntity, uniqueFields) + await models.DBHelper.save(Model, newEntity) + try { + await publishMessage('patch', resource, newEntity) + } catch (err) { + logger.logFullError(err) + } + return newEntity + } + + patch.schema = { + id: joi.string(), + entity: patchSchema, + auth: joi.object(), + params: joi.object() + } + + /** + * get device by id + * @param id the device id + * @param auth the auth obj + * @param params the path parameters + * @param query the query parameters + * @return {Promise} the db device + */ + async function get (id, auth, params, query = {}) { + let recordObj + // Merge path and query params + const trueParams = _.assign(params, query) + try { + const result = await esHelper.getFromElasticSearch(resource, id, auth, trueParams) + // check permission + permissionCheck(auth, result) + return result + } catch (err) { + // return error if enrich fails or permission fails + if ((resource === 'user' && trueParams.enrich) || (err.status && err.status === 403)) { + throw errors.elasticSearchEnrichError(err.message) + } + logger.logFullError(err) + } + if (_.isNil(trueParams) || _.isEmpty(trueParams)) { + recordObj = await models.DBHelper.get(Model, id) + } else { + const items = await models.DBHelper.find(Model, buildQueryByParams(trueParams)) + recordObj = items[0] + if (!recordObj) { + throw errors.newEntityNotFoundError(`cannot find ${Model.tableName} where ${_.map(trueParams, (v, k) => `${k}:${v}`).join(', ')}`) + } + } + permissionCheck(auth, recordObj) + return recordObj + } + + /** + * search devices by query + * @param query the search query + * @param auth the auth object + * @return {Promise} the results + */ + async function search (query, auth) { + try { + return await esHelper.searchElasticSearch(resource, query, auth) + } catch (err) { + // return error if enrich fails + if (resource === 'user' && query.enrich) { + throw errors.elasticSearchEnrichError(err.message) + } + logger.logFullError(err) + } + + // Elasticsearch failed. Hit the database + const dbQueries = await buildDBQuery(query, auth) + + // user token + // for non-admin users, this endpoint will only return entities that the user has created. + if ( + auth.roles && + !checkIfExists(auth.roles, appConst.AdminUser) && + !checkIfExists(auth.roles, [appConst.UserRoles.ubahn]) + ) { + dbQueries.push(`${Model.tableName}.createdBy = '${getAuthUser(auth)}'`) + } + const items = await models.DBHelper.find(Model, dbQueries) + // return fromDB:true to indicate it is got from db, + // and response headers ('X-Total', 'X-Page', etc.) are not set in this case + return { fromDb: true, result: items, total: items.length } + } + + search.schema = { + query: searchSchema, + auth: joi.object() + } + + /** + * remove entity by id + * @param id the entity id + * @param auth the auth object + * @param params the query params + * @return {Promise} no data returned + */ + async function remove (id, auth, params) { + let payload + await get(id, auth, params) // check exist + await models.DBHelper.delete(Model, id, buildQueryByParams(params)) + if (SUB_USER_DOCUMENTS[resource] || SUB_ORG_DOCUMENTS[resource]) { + payload = _.assign({}, params) + } else { + payload = { + id + } + } + try { + await publishMessage('remove', resource, payload) + } catch (err) { + logger.logFullError(err) + } + } + + return { + create, search, patch, get, remove + } +} + +module.exports = { + getServiceMethods +} diff --git a/src/consts.js b/src/consts.js new file mode 100644 index 0000000..18835a2 --- /dev/null +++ b/src/consts.js @@ -0,0 +1,33 @@ +/** + * roles that used in service, all roles must match topcoder roles + * Admin and Administrator are both admin user + */ +const UserRoles = { + admin: 'Admin', + administrator: 'Administrator', + topcoderUser: 'Topcoder User', + copilot: 'Copilot', + ubahn: 'u-bahn' +} +/** + * all authenticated users. + * @type {(string)[]} + */ +const AllAuthenticatedUsers = [ + UserRoles.admin, + UserRoles.administrator, + UserRoles.topcoderUser, + UserRoles.copilot, + UserRoles.ubahn +] + +/** + * all admin user + */ +const AdminUser = [UserRoles.admin, UserRoles.administrator] + +module.exports = { + UserRoles, + AllAuthenticatedUsers, + AdminUser +} diff --git a/src/models/Achievement.js b/src/models/Achievement.js new file mode 100644 index 0000000..f31684d --- /dev/null +++ b/src/models/Achievement.js @@ -0,0 +1,19 @@ +const { RecordObject } = require('./BaseObject') + +/** + * Achievement model + */ +class Achievement extends RecordObject { + constructor () { + super() + this.achievementsProviderId = null + this.name = null + this.uri = null + this.certifierId = null + this.certifiedDate = null + this.userId = null + } +} + +Achievement.tableName = 'Achievement' +module.exports = Achievement diff --git a/src/models/AchievementsProvider.js b/src/models/AchievementsProvider.js new file mode 100644 index 0000000..ab1db52 --- /dev/null +++ b/src/models/AchievementsProvider.js @@ -0,0 +1,17 @@ +const { RecordObject } = require('./BaseObject') + +/** + * AchievementsProvider model + */ +class AchievementsProvider extends RecordObject { + constructor () { + super() + this.name = null + } +} + +AchievementsProvider.tableName = 'AchievementsProvider' +AchievementsProvider.additionalSql = [ + 'CREATE INDEX ON AchievementsProvider (name)' +] +module.exports = AchievementsProvider diff --git a/src/models/Attribute.js b/src/models/Attribute.js new file mode 100644 index 0000000..c41b9ff --- /dev/null +++ b/src/models/Attribute.js @@ -0,0 +1,18 @@ +const { RecordObject } = require('./BaseObject') + +/** + * Attribute model + */ +class Attribute extends RecordObject { + constructor () { + super() + this.attributeGroupId = null + this.name = null + } +} + +Attribute.tableName = 'Attribute' +Attribute.additionalSql = [ + 'CREATE INDEX ON Attribute (name)' +] +module.exports = Attribute diff --git a/src/models/AttributeGroup.js b/src/models/AttributeGroup.js new file mode 100644 index 0000000..dbd1272 --- /dev/null +++ b/src/models/AttributeGroup.js @@ -0,0 +1,18 @@ +const { RecordObject } = require('./BaseObject') + +/** + * AttributeGroup model + */ +class AttributeGroup extends RecordObject { + constructor () { + super() + this.organizationId = null + this.name = null + } +} + +AttributeGroup.tableName = 'AttributeGroup' +AttributeGroup.additionalSql = [ + 'CREATE INDEX ON AttributeGroup (name)' +] +module.exports = AttributeGroup diff --git a/src/models/BaseObject.js b/src/models/BaseObject.js new file mode 100644 index 0000000..64a1a1f --- /dev/null +++ b/src/models/BaseObject.js @@ -0,0 +1,48 @@ +const _ = require('lodash') + +/** + * Base object + */ +class BaseObject { + constructor () { + this.id = null + } + + /** + * to json object + * @returns {*} + */ + toJSON () { + return _.omit(this) + } + + /** + * set value from obj + * @param obj the object value + */ + from (obj) { + const keys = _.keys(this) + _.each(keys, key => { + this[key] = obj[key] + }) + return this + } +} + +/** + * base object with created and updated etc . + */ +class RecordObject extends BaseObject { + constructor () { + super() + this.created = null + this.updated = null + this.createdBy = null + this.updatedBy = null + } +} + +module.exports = { + BaseObject, + RecordObject +} diff --git a/src/models/DBHelper.js b/src/models/DBHelper.js new file mode 100644 index 0000000..7862c4d --- /dev/null +++ b/src/models/DBHelper.js @@ -0,0 +1,191 @@ +const { readerToJson, writeValueAsIon } = require('../common/helper') + +const QLDB = require('amazon-qldb-driver-nodejs') +const config = require('config') +const _ = require('lodash') +const logger = require('../common/logger') +const uuid = require('uuid') +const errors = require('../common/errors') + +/** + * the database instance + */ +const qldbInstance = new QLDB.PooledQldbDriver(config.DATABASE, { + accessKeyId: config.AWS_ACCESS_KEY_ID, + secretAccessKey: config.AWS_SECRET_ACCESS_KEY, + region: config.AWS_REGION +}) + +/** + * Database helper + */ +class DBHelper { + /** + * get db connection session + * @returns {Promise} + */ + static async getSession () { + return qldbInstance.getSession() + } + + /** + * get model by id + * @param Model the model + * @param id the id + * @param attributes the attributes + * @returns {Promise<{}>} + */ + static async get (Model, id, attributes) { + const session = await this.getSession() + try { + const tn = Model.tableName + const sql = `SELECT ${_.isNil(attributes) || _.isEmpty(attributes) ? '*' : attributes.join(',')} FROM ${tn} WHERE id = '${id}'` + const result = await session.executeStatement(sql, []) + if (result.getResultList().length <= 0) { + throw errors.newEntityNotFoundError(`cannot find ${tn} where id = ${id}`) + } + return new Model().from(readerToJson(result.getResultList()[0])) + } finally { + session.close() + } + } + + /** + * delete item by id + * @param model the model + * @param id the id + * @param queries the db queries + * @returns {Promise} + */ + static async delete (model, id, queries = []) { + const session = await this.getSession() + try { + const tn = model.tableName + if (id) { + queries.push(`id = '${id}'`) + } + if (queries.length <= 0) { + throw errors.newBadRequestError('delete rows without queries is not allowed') + } + const sql = `DELETE FROM ${tn} where ${queries.join(' AND ')}` + logger.debug(sql) + await session.executeStatement(sql, []) + } finally { + session.close() + } + } + + /** + * find items by queries + * @param Model the model + * @param queries the sql queries + * @returns {Promise<{}[]>} + */ + static async find (Model, queries) { + const session = await this.getSession() + try { + const tn = Model.tableName + let sql = `SELECT * FROM ${tn}` + if (queries.length > 0 && queries[0].toLowerCase().indexOf('select') === 0) { + sql = queries.shift() + } + const where = queries.length <= 0 ? '' : ('WHERE ' + queries.join(' AND ')) + sql = `${sql} ${where}` + logger.debug(sql) + const results = await session.executeStatement(sql, []) + return results.getResultList().map(r => new Model().from(readerToJson(r))) + } finally { + session.close() + } + } + + /** + * save/insert item to database + * @param model the model + * @param entity the entity + * @param create is create + * @returns {Promise} + */ + static async save (model, entity, create) { + const session = await this.getSession() + try { + const tn = model.tableName + const documentsWriter = QLDB.createQldbWriter() + if (entity.id && !create) { + const statement = `UPDATE ${tn} AS p SET p = ? WHERE p.id = '${entity.id}'` + writeValueAsIon(entity.toJSON(), documentsWriter) + await session.executeStatement(statement, [documentsWriter]) + } else { + entity.id = entity.id || uuid.v4() + const statement = `INSERT INTO ${tn} ?` + writeValueAsIon(entity.toJSON(), documentsWriter) + await session.executeStatement(statement, [documentsWriter]) + } + } finally { + session.close() + } + return entity + } + + /** + * create table if not exist + * @param model the model + * @returns {Promise} + */ + static async createTable (model) { + const session = await this.getSession() + try { + const tables = await session.getTableNames() + const tn = model.tableName + if (!_.includes(tables, tn)) { + await session.executeStatement(`create table ${tn}`, []) + logger.info(`table ${tn} created ...`) + const additional = [`create index on ${tn} (id)`].concat(model.additionalSql || []) + for (let i = 0; i < additional.length; i++) { + logger.debug(additional[i]) + await session.executeStatement(additional[i]) + } + } else { + logger.info('exists, skip create table ' + tn) + } + } finally { + session.close() + } + } + + /** + * clear table + * @param model the model + * @returns {Promise} + */ + static async clear (model) { + const session = await qldbInstance.getSession() + try { + const transaction = await session.startTransaction() + await transaction.executeInline('DELETE FROM ' + model.tableName, []) + await transaction.commit() + logger.info(`${model.tableName} clean`) + } finally { + session.close() + } + } + + /** + * drop table + * @param model the model + * @returns {Promise} + */ + static async drop (model) { + const session = await qldbInstance.getSession() + try { + const transaction = await session.startTransaction() + await transaction.executeInline('drop table ' + model.tableName, []) + await transaction.commit() + logger.info(`table ${model.tableName} dropped`) + } finally { + session.close() + } + } +} + +module.exports = DBHelper diff --git a/src/models/ExternalProfile.js b/src/models/ExternalProfile.js new file mode 100644 index 0000000..a847499 --- /dev/null +++ b/src/models/ExternalProfile.js @@ -0,0 +1,19 @@ +const { RecordObject } = require('./BaseObject') + +/** + * ExternalProfile model + */ +class ExternalProfile extends RecordObject { + constructor () { + super() + this.userId = null + this.organizationId = null + this.externalId = null + this.uri = null + this.isInactive = null + } +} + +ExternalProfile.tableName = 'ExternalProfile' + +module.exports = ExternalProfile diff --git a/src/models/Organization.js b/src/models/Organization.js new file mode 100644 index 0000000..a8692a5 --- /dev/null +++ b/src/models/Organization.js @@ -0,0 +1,17 @@ +const { RecordObject } = require('./BaseObject') + +/** + * Organization model + */ +class Organization extends RecordObject { + constructor () { + super() + this.name = null + } +} + +Organization.tableName = 'Organization' +Organization.additionalSql = [ + 'CREATE INDEX ON Organization (name)' +] +module.exports = Organization diff --git a/src/models/OrganizationSkillsProvider.js b/src/models/OrganizationSkillsProvider.js new file mode 100644 index 0000000..afab78e --- /dev/null +++ b/src/models/OrganizationSkillsProvider.js @@ -0,0 +1,16 @@ +const { RecordObject } = require('./BaseObject') + +/** + * OrganizationSkillsProvider model + */ +class OrganizationSkillsProvider extends RecordObject { + constructor () { + super() + this.organizationId = null + this.skillProviderId = null + } +} + +OrganizationSkillsProvider.tableName = 'OrganizationSkillsProvider' + +module.exports = OrganizationSkillsProvider diff --git a/src/models/Role.js b/src/models/Role.js new file mode 100644 index 0000000..8f01963 --- /dev/null +++ b/src/models/Role.js @@ -0,0 +1,17 @@ +const { RecordObject } = require('./BaseObject') + +/** + * Role model + */ +class Role extends RecordObject { + constructor () { + super() + this.name = null + } +} + +Role.tableName = 'Role' +Role.additionalSql = [ + 'CREATE INDEX ON Role (name)' +] +module.exports = Role diff --git a/src/models/Skill.js b/src/models/Skill.js new file mode 100644 index 0000000..fd5bfe7 --- /dev/null +++ b/src/models/Skill.js @@ -0,0 +1,17 @@ +const { RecordObject } = require('./BaseObject') + +/** + * Skill model + */ +class Skill extends RecordObject { + constructor () { + super() + this.skillProviderId = null + this.name = null + this.externalId = null + this.uri = null + } +} + +Skill.tableName = 'Skill' +module.exports = Skill diff --git a/src/models/SkillsProvider.js b/src/models/SkillsProvider.js new file mode 100644 index 0000000..f37617d --- /dev/null +++ b/src/models/SkillsProvider.js @@ -0,0 +1,14 @@ +const { RecordObject } = require('./BaseObject') + +/** + * SkillsProvider model + */ +class SkillsProvider extends RecordObject { + constructor () { + super() + this.name = null + } +} + +SkillsProvider.tableName = 'SkillsProvider' +module.exports = SkillsProvider diff --git a/src/models/User.js b/src/models/User.js new file mode 100644 index 0000000..f089ccb --- /dev/null +++ b/src/models/User.js @@ -0,0 +1,23 @@ +const { RecordObject } = require('./BaseObject') + +/** + * User model + */ +class User extends RecordObject { + constructor () { + super() + this.handle = null + this.firstName = null + this.lastName = null + } +} + +/** + * User is keywords in database, so use `DUser` + * @type {string} + */ +User.tableName = 'DUser' +User.additionalSql = [ + 'CREATE INDEX ON DUser (handle)' +] +module.exports = User diff --git a/src/models/UserAttribute.js b/src/models/UserAttribute.js new file mode 100644 index 0000000..0d7dc0b --- /dev/null +++ b/src/models/UserAttribute.js @@ -0,0 +1,16 @@ +const { RecordObject } = require('./BaseObject') + +/** + * UsersAttribute skill model + */ +class UsersAttribute extends RecordObject { + constructor () { + super() + this.attributeId = null + this.value = null + this.userId = null + } +} + +UsersAttribute.tableName = 'UsersAttribute' +module.exports = UsersAttribute diff --git a/src/models/UsersRole.js b/src/models/UsersRole.js new file mode 100644 index 0000000..4e43824 --- /dev/null +++ b/src/models/UsersRole.js @@ -0,0 +1,16 @@ +const { RecordObject } = require('./BaseObject') + +/** + * UserRole model + */ +class UsersRole extends RecordObject { + constructor () { + super() + this.userId = null + this.roleId = null + } +} + +UsersRole.tableName = 'UserRole' + +module.exports = UsersRole diff --git a/src/models/UsersSkill.js b/src/models/UsersSkill.js new file mode 100644 index 0000000..6407d94 --- /dev/null +++ b/src/models/UsersSkill.js @@ -0,0 +1,18 @@ +const { RecordObject } = require('./BaseObject') + +/** + * users skill model + */ +class UsersSkill extends RecordObject { + constructor () { + super() + this.skillId = null + this.metricValue = null + this.certifierId = null + this.certifiedDate = null + this.userId = null + } +} + +UsersSkill.tableName = 'UsersSkill' +module.exports = UsersSkill diff --git a/src/models/index.js b/src/models/index.js new file mode 100755 index 0000000..8eecacc --- /dev/null +++ b/src/models/index.js @@ -0,0 +1,61 @@ +/** + * the model index + */ + +const User = require('./User') +const Role = require('./Role') +const SkillsProvider = require('./SkillsProvider') +const Skill = require('./Skill') +const Organization = require('./Organization') +const UsersRole = require('./UsersRole') +const UsersSkill = require('./UsersSkill') +const ExternalProfile = require('./ExternalProfile') +const AchievementsProvider = require('./AchievementsProvider') +const Achievement = require('./Achievement') +const AttributeGroup = require('./AttributeGroup') +const Attribute = require('./Attribute') +const UserAttribute = require('./UserAttribute') +const OrganizationSkillsProvider = require('./OrganizationSkillsProvider') +const logger = require('../common/logger') +const consts = require('../consts') + +const DBHelper = require('./DBHelper') + +module.exports = { + User, + Role, + SkillsProvider, + Organization, + Skill, + UsersRole, + UsersSkill, + Achievement, + ExternalProfile, + AchievementsProvider, + AttributeGroup, + Attribute, + UserAttribute, + OrganizationSkillsProvider, + consts, + DBHelper +} +/** + * create table + */ +module.exports.init = async () => { + logger.info('connect to database, check/create tables ...') + await DBHelper.createTable(User) + await DBHelper.createTable(Role) + await DBHelper.createTable(SkillsProvider) + await DBHelper.createTable(Skill) + await DBHelper.createTable(Organization) + await DBHelper.createTable(UsersRole) + await DBHelper.createTable(UsersSkill) + await DBHelper.createTable(ExternalProfile) + await DBHelper.createTable(AchievementsProvider) + await DBHelper.createTable(Achievement) + await DBHelper.createTable(AttributeGroup) + await DBHelper.createTable(Attribute) + await DBHelper.createTable(UserAttribute) + await DBHelper.createTable(OrganizationSkillsProvider) +} diff --git a/src/modules/achievement/controller.js b/src/modules/achievement/controller.js new file mode 100644 index 0000000..3a8cdb9 --- /dev/null +++ b/src/modules/achievement/controller.js @@ -0,0 +1,11 @@ +/** + * the achievement controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/achievement/route.js b/src/modules/achievement/route.js new file mode 100644 index 0000000..869309d --- /dev/null +++ b/src/modules/achievement/route.js @@ -0,0 +1,54 @@ +/** + * the achievement routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/users/:userId/achievements': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievement', 'all:achievement'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:achievement', 'all:achievement'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievement', 'all:achievement'] + } + }, + '/users/:userId/achievements/:achievementsProviderId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievement', 'all:achievement'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievement', 'all:achievement'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:achievement', 'all:achievement'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:achievement', 'all:achievement'] + } + } +} diff --git a/src/modules/achievement/service.js b/src/modules/achievement/service.js new file mode 100644 index 0000000..e57fcaf --- /dev/null +++ b/src/modules/achievement/service.js @@ -0,0 +1,45 @@ +/** + * the achievement services + */ + +const joi = require('@hapi/joi').extend(require('@hapi/joi-date')) +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.Achievement, + { // create request body joi schema + userId: joi.string().required(), + achievementsProviderId: joi.string().required(), + name: joi.string().required(), + uri: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date().format('iso') + }, + { // patch request body joi schema + userId: joi.string().required(), + achievementsProviderId: joi.string().required(), + name: joi.string(), + uri: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date().format('iso') + }, + { // search request query joi schema + userId: joi.string().required(), + achievementsProviderName: joi.string() + }, + async (query) => { // build search query by request + const dbQueries = ['SELECT * FROM AchievementsProvider, Achievement', + `Achievement.userId = '${query.userId}'`, + 'Achievement.achievementsProviderId = AchievementsProvider.id'] + // filter by achievements provider name + if (query.achievementsProviderName) { + dbQueries.push(`AchievementsProvider.name like '%${query.achievementsProviderName}%'`) + } + return dbQueries + }, + [['userId', 'achievementsProviderId']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/achievementsProvider/controller.js b/src/modules/achievementsProvider/controller.js new file mode 100644 index 0000000..cb17cf2 --- /dev/null +++ b/src/modules/achievementsProvider/controller.js @@ -0,0 +1,11 @@ +/** + * the achievement provider controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/achievementsProvider/route.js b/src/modules/achievementsProvider/route.js new file mode 100644 index 0000000..140702b --- /dev/null +++ b/src/modules/achievementsProvider/route.js @@ -0,0 +1,54 @@ +/** + * the achievements provider routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/achievementsProviders': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievementsProvider', 'all:achievementsProvider'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:achievementsProvider', 'all:achievementsProvider'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievementsProvider', 'all:achievementsProvider'] + } + }, + '/achievementsProviders/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievementsProvider', 'all:achievementsProvider'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:achievementsProvider', 'all:achievementsProvider'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:achievementsProvider', 'all:achievementsProvider'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:achievementsProvider', 'all:achievementsProvider'] + } + } +} diff --git a/src/modules/achievementsProvider/service.js b/src/modules/achievementsProvider/service.js new file mode 100644 index 0000000..bd6246c --- /dev/null +++ b/src/modules/achievementsProvider/service.js @@ -0,0 +1,32 @@ +/** + * the achievements provider services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.AchievementsProvider, + { // create request body joi schema + name: joi.string().required() + }, + { // patch request body joi schema + name: joi.string() + }, + { // search request query joi schema + name: joi.string() + }, + async (query) => { // build search query by request + const dbQueries = [] + // filter by provider name + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + return dbQueries + }, + [['name']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/attribute/controller.js b/src/modules/attribute/controller.js new file mode 100644 index 0000000..a868eff --- /dev/null +++ b/src/modules/attribute/controller.js @@ -0,0 +1,11 @@ +/** + * the attribute controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/attribute/route.js b/src/modules/attribute/route.js new file mode 100644 index 0000000..ccf8edd --- /dev/null +++ b/src/modules/attribute/route.js @@ -0,0 +1,54 @@ +/** + * the attribute routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/attributes': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attribute', 'all:attribute'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:attribute', 'all:attribute'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attribute', 'all:attribute'] + } + }, + '/attributes/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attribute', 'all:attribute'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attribute', 'all:attribute'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:attribute', 'all:attribute'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:attribute', 'all:attribute'] + } + } +} diff --git a/src/modules/attribute/service.js b/src/modules/attribute/service.js new file mode 100644 index 0000000..7a607a2 --- /dev/null +++ b/src/modules/attribute/service.js @@ -0,0 +1,39 @@ +/** + * the attribute group services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.Attribute, + { // create request body joi schema + name: joi.string().required(), + attributeGroupId: joi.string().required() + }, + { // patch request body joi schema + name: joi.string(), + attributeGroupId: joi.string() + }, + { // search request query joi schema + name: joi.string(), + attributeGroupId: joi.string() + }, + async (query) => { // build search query by request + const dbQueries = [] + // filter by provider name + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + // filter by attribute group id + if (query.attributeGroupId) { + dbQueries.push(`attributeGroupId = '${query.attributeGroupId}'`) + } + return dbQueries + }, + [['name', 'attributeGroupId']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/attributeGroup/controller.js b/src/modules/attributeGroup/controller.js new file mode 100644 index 0000000..540c3d1 --- /dev/null +++ b/src/modules/attributeGroup/controller.js @@ -0,0 +1,11 @@ +/** + * the attribute group controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/attributeGroup/route.js b/src/modules/attributeGroup/route.js new file mode 100644 index 0000000..53237fd --- /dev/null +++ b/src/modules/attributeGroup/route.js @@ -0,0 +1,54 @@ +/** + * the attribute group routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/attributeGroups': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attributeGroup', 'all:attributeGroup'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:attributeGroup', 'all:attributeGroup'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attributeGroup', 'all:attributeGroup'] + } + }, + '/attributeGroups/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attributeGroup', 'all:attributeGroup'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:attributeGroup', 'all:attributeGroup'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:attributeGroup', 'all:attributeGroup'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:attributeGroup', 'all:attributeGroup'] + } + } +} diff --git a/src/modules/attributeGroup/service.js b/src/modules/attributeGroup/service.js new file mode 100644 index 0000000..f8b776f --- /dev/null +++ b/src/modules/attributeGroup/service.js @@ -0,0 +1,38 @@ +/** + * the attribute group provider services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.AttributeGroup, + { // create request body joi schema + name: joi.string().required(), + organizationId: joi.string().required() + }, + { // patch request body joi schema + name: joi.string(), + organizationId: joi.string() + }, + { // search request query joi schema + name: joi.string(), + organizationId: joi.string() + }, + async (query) => { // build search query by request + const dbQueries = [] + // filter by provider name + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + if (query.organizationId) { + dbQueries.push(`organizationId = '${query.organizationId}'`) + } + return dbQueries + }, + [['name']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/externalProfile/controller.js b/src/modules/externalProfile/controller.js new file mode 100644 index 0000000..a8b6fee --- /dev/null +++ b/src/modules/externalProfile/controller.js @@ -0,0 +1,11 @@ +/** + * the external profile controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/externalProfile/route.js b/src/modules/externalProfile/route.js new file mode 100644 index 0000000..b5512f8 --- /dev/null +++ b/src/modules/externalProfile/route.js @@ -0,0 +1,54 @@ +/** + * the external profile routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/users/:userId/externalProfiles': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:externalProfile', 'all:externalProfile'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:externalProfile', 'all:externalProfile'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:externalProfile', 'all:externalProfile'] + } + }, + '/users/:userId/externalProfiles/:organizationId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:externalProfile', 'all:externalProfile'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:externalProfile', 'all:externalProfile'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:externalProfile', 'all:externalProfile'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:externalProfile', 'all:externalProfile'] + } + } +} diff --git a/src/modules/externalProfile/service.js b/src/modules/externalProfile/service.js new file mode 100644 index 0000000..5116710 --- /dev/null +++ b/src/modules/externalProfile/service.js @@ -0,0 +1,52 @@ +/** + * the external profile services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.ExternalProfile, + { // create request body joi schema + userId: joi.string().required(), + organizationId: joi.string().required(), + externalId: joi.string().required(), + uri: joi.string(), + isInactive: joi.boolean().default(false) + }, + { // patch request body joi schema + userId: joi.string().required(), + organizationId: joi.string().required(), + externalId: joi.string(), + uri: joi.string(), + isInactive: joi.boolean() + }, + { // search request query joi schema + userId: joi.string().required(), + externalId: joi.string(), + organizationName: joi.string(), + isInactive: joi.boolean() + }, + async (query) => { // build search query by request + const dbQueries = ['SELECT * FROM Organization, ExternalProfile', + `ExternalProfile.userId = '${query.userId}'`, + 'ExternalProfile.organizationId = Organization.id'] + // filter by organization name + if (query.organizationName) { + dbQueries.push(`Organization.name like '%${query.organizationName}%'`) + } + if (query.externalId) { + dbQueries.push(`ExternalProfile.externalId = '${query.externalId}'`) + } + if (query.isInactive) { + dbQueries.push(`ExternalProfile.isInactive = '${query.isInactive}'`) + } + + return dbQueries + }, + [['userId', 'organizationId']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/health/controller.js b/src/modules/health/controller.js new file mode 100644 index 0000000..cfd94c2 --- /dev/null +++ b/src/modules/health/controller.js @@ -0,0 +1,11 @@ +/** + * the role controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/health/route.js b/src/modules/health/route.js new file mode 100644 index 0000000..8b11000 --- /dev/null +++ b/src/modules/health/route.js @@ -0,0 +1,12 @@ +/** + * the health routes + */ + +const Controller = require('./controller') +module.exports = { + '/ubahnapi/health': { + get: { + method: Controller.get + } + } +} diff --git a/src/modules/health/service.js b/src/modules/health/service.js new file mode 100644 index 0000000..d1cf1fe --- /dev/null +++ b/src/modules/health/service.js @@ -0,0 +1,17 @@ +const errors = require('../../common/errors') +const models = require('../../models') + +async function get () { + // Check QLDB Connection by retrieving a session + try { + const session = await models.DBHelper.getSession() + + session.close() + } catch (e) { + throw errors.serviceUnavailableError(`QLDB is unavailable, ${e.message}`) + } + + return { checksRun: 1 } +} + +module.exports = { get } diff --git a/src/modules/organization/controller.js b/src/modules/organization/controller.js new file mode 100644 index 0000000..22e6990 --- /dev/null +++ b/src/modules/organization/controller.js @@ -0,0 +1,11 @@ +/** + * the organization controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/organization/route.js b/src/modules/organization/route.js new file mode 100644 index 0000000..1af15e9 --- /dev/null +++ b/src/modules/organization/route.js @@ -0,0 +1,54 @@ +/** + * the organization routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/organizations': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organization', 'all:organization'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:organization', 'all:organization'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organization', 'all:organization'] + } + }, + '/organizations/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organization', 'all:organization'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organization', 'all:organization'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:organization', 'all:organization'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:organization', 'all:organization'] + } + } +} diff --git a/src/modules/organization/service.js b/src/modules/organization/service.js new file mode 100644 index 0000000..191214d --- /dev/null +++ b/src/modules/organization/service.js @@ -0,0 +1,23 @@ +/** + * the organizations services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.Organization, + { name: joi.string().required() }, + { name: joi.string() }, + { name: joi.string() }, + async query => { + const dbQueries = [] + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + return dbQueries + }, [['name']]) + +module.exports = { + ...methods +} diff --git a/src/modules/organizationSkillsProvider/controller.js b/src/modules/organizationSkillsProvider/controller.js new file mode 100644 index 0000000..7b2625b --- /dev/null +++ b/src/modules/organizationSkillsProvider/controller.js @@ -0,0 +1,11 @@ +/** + * the organization skills provider controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/organizationSkillsProvider/route.js b/src/modules/organizationSkillsProvider/route.js new file mode 100644 index 0000000..d8016b9 --- /dev/null +++ b/src/modules/organizationSkillsProvider/route.js @@ -0,0 +1,48 @@ +/** + * the organization skills provider routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/organizations/:organizationId/skillProviders': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organizationSkillsProvider', 'all:organizationSkillsProvider'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:organizationSkillsProvider', 'all:organizationSkillsProvider'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organizationSkillsProvider', 'all:organizationSkillsProvider'] + } + }, + '/organizations/:organizationId/skillProviders/:skillProviderId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organizationSkillsProvider', 'all:organizationSkillsProvider'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:organizationSkillsProvider', 'all:organizationSkillsProvider'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:organizationSkillsProvider', 'all:organizationSkillsProvider'] + } + } +} diff --git a/src/modules/organizationSkillsProvider/service.js b/src/modules/organizationSkillsProvider/service.js new file mode 100644 index 0000000..2a64c68 --- /dev/null +++ b/src/modules/organizationSkillsProvider/service.js @@ -0,0 +1,26 @@ +/** + * the organization skills provider services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.OrganizationSkillsProvider, + { + organizationId: joi.string().required(), + skillProviderId: joi.string().required() + }, + { + organizationId: joi.string(), + skillProviderId: joi.string() + }, + { + organizationId: joi.string().required() + }, + async (query) => [`organizationId = '${query.organizationId}'`], + [['organizationId', 'skillProviderId']]) + +module.exports = { + ...methods +} diff --git a/src/modules/role/controller.js b/src/modules/role/controller.js new file mode 100644 index 0000000..cfd94c2 --- /dev/null +++ b/src/modules/role/controller.js @@ -0,0 +1,11 @@ +/** + * the role controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/role/route.js b/src/modules/role/route.js new file mode 100644 index 0000000..44b9f6a --- /dev/null +++ b/src/modules/role/route.js @@ -0,0 +1,54 @@ +/** + * the roles routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/roles': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:role', 'all:role'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:role', 'all:role'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:role', 'all:role'] + } + }, + '/roles/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:role', 'all:role'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:role', 'all:role'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:role', 'all:role'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:role', 'all:role'] + } + } +} diff --git a/src/modules/role/service.js b/src/modules/role/service.js new file mode 100644 index 0000000..b8a34f1 --- /dev/null +++ b/src/modules/role/service.js @@ -0,0 +1,23 @@ +/** + * the roles services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.Role, + { name: joi.string().required() }, + { name: joi.string() }, + { name: joi.string() }, + async query => { + const dbQueries = [] + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + return dbQueries + }, [['name']]) + +module.exports = { + ...methods +} diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js new file mode 100644 index 0000000..5412aec --- /dev/null +++ b/src/modules/search/controller.js @@ -0,0 +1,46 @@ +/** + * The search controller + */ +const esHelper = require('../../common/es-helper') +const { injectSearchMeta } = require('../../common/helper') +const service = require('./service') + +/** + * Search for users. Returns enriched users + */ +async function searchUsers (req, res) { + const result = await esHelper.searchUsers(req.auth, req.body, req.query) + injectSearchMeta(req, res, result) + res.send(result.result) +} + +/** + * Search for skills in organization + */ +async function searchSkills (req, res) { + const result = await service.getSkills(req.query, req.auth) + res.send(result.result) +} + +/** + * Search for attribute values + */ +async function searchAttributeValues (req, res) { + const result = await esHelper.searchAttributeValues(req.query) + res.send(result.result) +} + +/** + * Search for achievement values + */ +async function searchAchievementValues (req, res) { + const result = await esHelper.searchAchievementValues(req.query) + res.send(result.result) +} + +module.exports = { + searchUsers, + searchSkills, + searchAttributeValues, + searchAchievementValues +} diff --git a/src/modules/search/route.js b/src/modules/search/route.js new file mode 100644 index 0000000..4eb0c08 --- /dev/null +++ b/src/modules/search/route.js @@ -0,0 +1,41 @@ +/** + * the search routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') + +module.exports = { + '/search/users': { + post: { + method: Controller.searchUsers, + auth: 'jwt', + access: [...consts.AdminUser, consts.UserRoles.ubahn], + scopes: ['read:user', 'all:user'] + } + }, + '/search/skills': { + get: { + method: Controller.searchSkills, + auth: 'jwt', + access: [...consts.AdminUser, consts.UserRoles.ubahn], + scopes: ['create:userAttribute', 'all:userAttribute'] + } + }, + '/search/userAttributes': { + get: { + method: Controller.searchAttributeValues, + auth: 'jwt', + access: [...consts.AdminUser, consts.UserRoles.ubahn], + scopes: ['create:userAttribute', 'all:userAttribute'] + } + }, + '/search/userAchievements': { + get: { + method: Controller.searchAchievementValues, + auth: 'jwt', + access: [...consts.AdminUser, consts.UserRoles.ubahn], + scopes: ['create:userAttribute', 'all:userAttribute'] + } + } +} diff --git a/src/modules/search/service.js b/src/modules/search/service.js new file mode 100644 index 0000000..0a5698a --- /dev/null +++ b/src/modules/search/service.js @@ -0,0 +1,84 @@ +const axios = require('axios') +const config = require('config') +const querystring = require('querystring') +const NodeCache = require('node-cache') +const joi = require('@hapi/joi') +const orgSkillsProviderService = require('../organizationSkillsProvider/service') +const esHelper = require('../../common/es-helper') + +// cache the emsi token +const tokenCache = new NodeCache() + +/** + * Format EMSI skills like the ones returned by the ES query + * @param {Array} emsiSkills Array of skills + */ +function formatEmsiSkills (emsiSkills) { + return emsiSkills.skills.slice(0, 10).map(skill => ({ + name: skill.name, + skillId: skill.id, + skillProviderId: config.EMSI_SKILLPROVIDER_ID + })) +} + +/** + * Get emsi token + * @returns {string} the emsi token + */ +async function getEmsiToken () { + let token = tokenCache.get('emsi_token') + if (!token) { + const res = await axios.post(config.EMSI.AUTH_URL, querystring.stringify({ + client_id: config.EMSI.CLIENT_ID, + client_secret: config.EMSI.CLIENT_SECRET, + grant_type: config.EMSI.GRANT_TYPE, + scope: config.EMSI.SCOPE + })) + token = res.data.access_token + tokenCache.set('emsi_token', token, res.data.expires_in) + } + return token +} + +/** + * Get data from emsi + * @param {String} path the emsi endpoint path + * @param {String} params get params + * @returns {Object} response data + */ +async function getEmsiObject (path, params) { + const token = await getEmsiToken() + const res = await axios.get(`${config.EMSI.BASE_URL}${path}`, { params, headers: { authorization: `Bearer ${token}` } }) + return res.data +} + +/** + * Get skills by query. +// !Proxies request to EMSI if org uses it as its skill provider + * @param {String} query the query + * @param {Object} auth the auth object + * @returns {Object} the Object with skills + */ +async function getSkills (query, auth) { + let result + const skillProviderIds = await orgSkillsProviderService.search({ organizationId: query.organizationId }, auth) + + if (skillProviderIds.result.length === 1 && skillProviderIds.result[0].skillProviderId === config.EMSI_SKILLPROVIDER_ID) { + result = await getEmsiObject('/skills', { q: query.keyword }) + return { result: formatEmsiSkills(result) } + } + + result = await esHelper.searchSkillsInOrganization(query) + + return result +} + +getSkills.schema = { + query: joi.object().keys({ + organizationId: joi.string().required(), + keyword: joi.string().required() + }), + auth: joi.object() +} + +module.exports = { getSkills } diff --git a/src/modules/skill/controller.js b/src/modules/skill/controller.js new file mode 100644 index 0000000..e42c58f --- /dev/null +++ b/src/modules/skill/controller.js @@ -0,0 +1,11 @@ +/** + * the skill controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/skill/route.js b/src/modules/skill/route.js new file mode 100644 index 0000000..d151d4b --- /dev/null +++ b/src/modules/skill/route.js @@ -0,0 +1,54 @@ +/** + * the skill routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/skills': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skill', 'all:skill'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:skill', 'all:skill'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skill', 'all:skill'] + } + }, + '/skills/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skill', 'all:skill'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skill', 'all:skill'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:skill', 'all:skill'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:skill', 'all:skill'] + } + } +} diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js new file mode 100644 index 0000000..a869e88 --- /dev/null +++ b/src/modules/skill/service.js @@ -0,0 +1,45 @@ +/** + * the skill services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.Skill, + { + skillProviderId: joi.string().required(), + name: joi.string().required(), + uri: joi.string(), + externalId: joi.string() + }, + { + skillProviderId: joi.string(), + name: joi.string(), + uri: joi.string(), + externalId: joi.string() + }, + { + skillProviderId: joi.string(), + name: joi.string(), + externalId: joi.string() + }, + async query => { + const dbQueries = [] + if (query.externalId) { + dbQueries.push(`externalId like '%${query.externalId}%'`) + } + if (query.skillProviderId) { + dbQueries.push(`skillProviderId like '%${query.skillProviderId}%'`) + } + if (query.name) { + dbQueries.push(`name = '${query.skillProviderId}'`) + } + return dbQueries + }, + [['skillProviderId', 'externalId', 'name']] +) + +module.exports = { + ...methods +} diff --git a/src/modules/skillsProvider/controller.js b/src/modules/skillsProvider/controller.js new file mode 100644 index 0000000..4a728ec --- /dev/null +++ b/src/modules/skillsProvider/controller.js @@ -0,0 +1,11 @@ +/** + * the skillsProvider controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/skillsProvider/route.js b/src/modules/skillsProvider/route.js new file mode 100644 index 0000000..a5e15a4 --- /dev/null +++ b/src/modules/skillsProvider/route.js @@ -0,0 +1,54 @@ +/** + * the skillsProvider routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/skillsProviders': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skillsProvider', 'all:skillsProvider'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:skillsProvider', 'all:skillsProvider'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skillsProvider', 'all:skillsProvider'] + } + }, + '/skillsProviders/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skillsProvider', 'all:skillsProvider'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:skillsProvider', 'all:skillsProvider'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:skillsProvider', 'all:skillsProvider'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:skillsProvider', 'all:skillsProvider'] + } + } +} diff --git a/src/modules/skillsProvider/service.js b/src/modules/skillsProvider/service.js new file mode 100644 index 0000000..7dd9f55 --- /dev/null +++ b/src/modules/skillsProvider/service.js @@ -0,0 +1,23 @@ +/** + * the skillsProvider services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.SkillsProvider, + { name: joi.string().required() }, + { name: joi.string() }, + { name: joi.string() }, + async query => { + const dbQueries = [] + if (query.name) { + dbQueries.push(`name like '%${query.name}%'`) + } + return dbQueries + }) + +module.exports = { + ...methods +} diff --git a/src/modules/user/controller.js b/src/modules/user/controller.js new file mode 100644 index 0000000..59312c9 --- /dev/null +++ b/src/modules/user/controller.js @@ -0,0 +1,11 @@ +/** + * the user controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/user/route.js b/src/modules/user/route.js new file mode 100644 index 0000000..0aac9fe --- /dev/null +++ b/src/modules/user/route.js @@ -0,0 +1,55 @@ +/** + * the user routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') + +module.exports = { + '/users': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:user', 'all:user'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:user', 'all:user'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:user', 'all:user'] + } + }, + '/users/:id': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:user', 'all:user'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:user', 'all:user'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:user', 'all:user'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:user', 'all:user'] + } + } +} diff --git a/src/modules/user/service.js b/src/modules/user/service.js new file mode 100644 index 0000000..c7160a8 --- /dev/null +++ b/src/modules/user/service.js @@ -0,0 +1,38 @@ +/** + * the users services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.User, + { + handle: joi.string().required(), + firstName: joi.string().required(), + lastName: joi.string().required() + }, + { + handle: joi.string(), + firstName: joi.string(), + lastName: joi.string() + }, + { handle: joi.string(), roleId: joi.string() }, + async query => { + let prefix = 'select * from DUser' + const dbQueries = [] + if (query.handle) { + dbQueries.push(`DUser.handle like '%${query.handle}%'`) + } + if (query.roleId) { + dbQueries.push(`Role.id = '${query.roleId}'`) + prefix = 'select * from Role, UserRole, DUser' + dbQueries.push('UserRole.userId = DUser.id') + dbQueries.push('UserRole.roleId = Role.id') + } + return [prefix].concat(dbQueries) + }, [['handle']]) + +module.exports = { + ...methods +} diff --git a/src/modules/usersAttribute/controller.js b/src/modules/usersAttribute/controller.js new file mode 100644 index 0000000..0dcf21d --- /dev/null +++ b/src/modules/usersAttribute/controller.js @@ -0,0 +1,11 @@ +/** + * the user attribute controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/usersAttribute/route.js b/src/modules/usersAttribute/route.js new file mode 100644 index 0000000..43937df --- /dev/null +++ b/src/modules/usersAttribute/route.js @@ -0,0 +1,54 @@ +/** + * the user attribute routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/users/:userId/attributes': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:userAttribute', 'all:userAttribute'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:userAttribute', 'all:userAttribute'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:userAttribute', 'all:userAttribute'] + } + }, + '/users/:userId/attributes/:attributeId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:userAttribute', 'all:userAttribute'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:userAttribute', 'all:userAttribute'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:userAttribute', 'all:userAttribute'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:userAttribute', 'all:userAttribute'] + } + } +} diff --git a/src/modules/usersAttribute/service.js b/src/modules/usersAttribute/service.js new file mode 100644 index 0000000..141cbac --- /dev/null +++ b/src/modules/usersAttribute/service.js @@ -0,0 +1,50 @@ +/** + * the user attribute services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.UserAttribute, + { // create request body joi schema + userId: joi.string().required(), + attributeId: joi.string().required(), + value: joi.string().required() + }, + { // patch request body joi schema + userId: joi.string().required(), + attributeId: joi.string().required(), + value: joi.string() + }, + { // search request query joi schema + userId: joi.string().required(), + attributeName: joi.string(), + attributeGroupName: joi.string(), + attributeGroupId: joi.string() + }, + async (query) => { // build search query by request + const dbQueries = ['SELECT * FROM Attribute, AttributeGroup, UserAttribute', + `UserAttribute.userId = '${query.userId}'`, + 'UserAttribute.attributeId=Attribute.id', + 'Attribute.attributeGroupId = AttributeGroup.id'] + // filter by attribute name + if (query.attributeName) { + dbQueries.push(`Attribute.name like '%${query.attributeName}%'`) + } + // filter by attribute group name + if (query.attributeGroupName) { + dbQueries.push(`AttributeGroup.name like '%${query.attributeGroupName}%'`) + } + // filter by attribute group id + if (query.attributeGroupId) { + dbQueries.push(`AttributeGroup.id = '${query.attributeGroupId}'`) + } + return dbQueries + }, + [['userId', 'attributeId']] // unique fields +) + +module.exports = { + ...methods +} diff --git a/src/modules/usersRole/controller.js b/src/modules/usersRole/controller.js new file mode 100644 index 0000000..6642c07 --- /dev/null +++ b/src/modules/usersRole/controller.js @@ -0,0 +1,11 @@ +/** + * the users role controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/usersRole/route.js b/src/modules/usersRole/route.js new file mode 100644 index 0000000..7ec1fee --- /dev/null +++ b/src/modules/usersRole/route.js @@ -0,0 +1,48 @@ +/** + * the users role routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/users/:userId/roles': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersRole', 'all:usersRole'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:usersRole', 'all:usersRole'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersRole', 'all:usersRole'] + } + }, + '/users/:userId/roles/:roleId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersRole', 'all:usersRole'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersRole', 'all:usersRole'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['delete:usersRole', 'all:usersRole'] + } + } +} diff --git a/src/modules/usersRole/service.js b/src/modules/usersRole/service.js new file mode 100644 index 0000000..743250a --- /dev/null +++ b/src/modules/usersRole/service.js @@ -0,0 +1,26 @@ +/** + * the skill services + */ + +const joi = require('@hapi/joi') +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.UsersRole, + { + userId: joi.string().required(), + roleId: joi.string().required() + }, + { + userId: joi.string(), + roleId: joi.string() + }, + { + userId: joi.string().required() + }, + async (query) => [`userId = '${query.userId}'`], + [['userId', 'roleId']]) + +module.exports = { + ...methods +} diff --git a/src/modules/usersSkill/controller.js b/src/modules/usersSkill/controller.js new file mode 100644 index 0000000..957662f --- /dev/null +++ b/src/modules/usersSkill/controller.js @@ -0,0 +1,11 @@ +/** + * the users skill controller + */ + +const service = require('./service') +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) + +module.exports = { + ...methods +} diff --git a/src/modules/usersSkill/route.js b/src/modules/usersSkill/route.js new file mode 100644 index 0000000..67cdc98 --- /dev/null +++ b/src/modules/usersSkill/route.js @@ -0,0 +1,54 @@ +/** + * the users skill routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') +module.exports = { + '/users/:userId/skills': { + get: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersSkill', 'all:usersSkill'] + }, + post: { + method: Controller.create, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['create:usersSkill', 'all:usersSkill'] + }, + head: { + method: Controller.search, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersSkill', 'all:usersSkill'] + } + }, + '/users/:userId/skills/:skillId': { + get: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersSkill', 'all:usersSkill'] + }, + head: { + method: Controller.get, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['read:usersSkill', 'all:usersSkill'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update:usersSkill', 'all:usersSkill'] + }, + delete: { + method: Controller.remove, + auth: 'jwt', + access: [...consts.AdminUser, consts.UserRoles.ubahn], + scopes: ['delete:usersSkill', 'all:usersSkill'] + } + } +} diff --git a/src/modules/usersSkill/service.js b/src/modules/usersSkill/service.js new file mode 100644 index 0000000..21f113d --- /dev/null +++ b/src/modules/usersSkill/service.js @@ -0,0 +1,38 @@ +/** + * the users skill services + */ + +const joi = require('@hapi/joi').extend(require('@hapi/joi-date')) +const models = require('../../models/index') +const helper = require('../../common/helper') +const methods = helper.getServiceMethods( + models.UsersSkill, + { + userId: joi.string().required(), + skillId: joi.string().required(), + metricValue: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date().format('iso') + }, + { + userId: joi.string(), + skillId: joi.string(), + metricValue: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date() + }, + { + userId: joi.string().required(), + skillName: joi.string() + }, + async (query) => { + const dbQueries = ['SELECT * FROM Skill, UsersSkill', `UsersSkill.userId = '${query.userId}'`, 'UsersSkill.skillId = Skill.id'] + if (query.skillName) { + dbQueries.push(`Skill.name like '%${query.skillName}%'`) + } + return dbQueries + }, [['userId', 'skillId']]) + +module.exports = { + ...methods +} diff --git a/src/route.js b/src/route.js new file mode 100755 index 0000000..45816c5 --- /dev/null +++ b/src/route.js @@ -0,0 +1,29 @@ +/** + * Defines the API routes + */ + +const _ = require('lodash') +const path = require('path') +const fs = require('fs') + +/** + * scan folder to find routes + * @param dir the scan base dir + */ +function searchRoutes (dir) { + const files = fs.readdirSync(dir) + let routes = {} + for (let i = 0; i < files.length; i += 1) { + const file = files[i] + const curPath = path.join(dir, file) + const stats = fs.statSync(curPath) + if (stats.isDirectory()) { + routes = _.extend({}, routes, searchRoutes(curPath)) + } else if (file.toLowerCase().indexOf('route.js') >= 0) { + routes = _.extend({}, routes, require(curPath)); // eslint-disable-line + } + } + return routes +} + +module.exports = searchRoutes(path.join(__dirname, '.'))