From 0a3c163c7a6a6c17975cfd1e708d5de1a228f2ef Mon Sep 17 00:00:00 2001 From: CWD Date: Tue, 5 May 2020 16:20:18 -0400 Subject: [PATCH 01/93] adding in mock svc --- .swagger-codegen-ignore | 23 + .swagger-codegen/VERSION | 1 + Procfile | 1 + README.md | 20 + api/swagger.yaml | 3682 ++++++++++++++++++++ controllers/Achievements.js | 89 + controllers/AchievementsProvider.js | 82 + controllers/AttributeGroups.js | 84 + controllers/Attributes.js | 82 + controllers/ExternalProfiles.js | 88 + controllers/Organizations.js | 82 + controllers/Roles.js | 82 + controllers/Skills.js | 80 + controllers/SkillsProvider.js | 82 + controllers/UserAttributes.js | 93 + controllers/UserRoles.js | 74 + controllers/Users.js | 88 + controllers/UsersSkills.js | 89 + docs/UBahn_API_Swagger_Specification.yaml | 3717 +++++++++++++++++++++ index.js | 44 + package-lock.json | 1084 ++++++ package.json | 20 + service/AchievementsProviderService.js | 118 + service/AchievementsService.js | 125 + service/AttributeGroupsService.js | 120 + service/AttributesService.js | 118 + service/ExternalProfilesService.js | 124 + service/OrganizationsService.js | 118 + service/RolesService.js | 118 + service/SkillsProviderService.js | 118 + service/SkillsService.js | 116 + service/UserAttributesService.js | 129 + service/UserRolesService.js | 102 + service/UsersService.js | 124 + service/UsersSkillsService.js | 125 + utils/writer.js | 43 + 36 files changed, 11285 insertions(+) create mode 100644 .swagger-codegen-ignore create mode 100644 .swagger-codegen/VERSION create mode 100644 Procfile create mode 100644 api/swagger.yaml create mode 100644 controllers/Achievements.js create mode 100644 controllers/AchievementsProvider.js create mode 100644 controllers/AttributeGroups.js create mode 100644 controllers/Attributes.js create mode 100644 controllers/ExternalProfiles.js create mode 100644 controllers/Organizations.js create mode 100644 controllers/Roles.js create mode 100644 controllers/Skills.js create mode 100644 controllers/SkillsProvider.js create mode 100644 controllers/UserAttributes.js create mode 100644 controllers/UserRoles.js create mode 100644 controllers/Users.js create mode 100644 controllers/UsersSkills.js create mode 100644 docs/UBahn_API_Swagger_Specification.yaml create mode 100644 index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 service/AchievementsProviderService.js create mode 100644 service/AchievementsService.js create mode 100644 service/AttributeGroupsService.js create mode 100644 service/AttributesService.js create mode 100644 service/ExternalProfilesService.js create mode 100644 service/OrganizationsService.js create mode 100644 service/RolesService.js create mode 100644 service/SkillsProviderService.js create mode 100644 service/SkillsService.js create mode 100644 service/UserAttributesService.js create mode 100644 service/UserRolesService.js create mode 100644 service/UsersService.js create mode 100644 service/UsersSkillsService.js create mode 100644 utils/writer.js diff --git a/.swagger-codegen-ignore b/.swagger-codegen-ignore new file mode 100644 index 0000000..c5fa491 --- /dev/null +++ b/.swagger-codegen-ignore @@ -0,0 +1,23 @@ +# Swagger Codegen Ignore +# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/.swagger-codegen/VERSION b/.swagger-codegen/VERSION new file mode 100644 index 0000000..9c84656 --- /dev/null +++ b/.swagger-codegen/VERSION @@ -0,0 +1 @@ +2.4.13 \ No newline at end of file diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..5ec9cc2 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: node index.js \ No newline at end of file diff --git a/README.md b/README.md index a476720..3b22396 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ # u-banh-api Universal Identity API + +## Swagger generated server + +### Overview +This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. + +#### Running the server +To run the server, run: + +``` +npm start +``` + +To view the Swagger UI interface: + +``` +open http://localhost:8080/docs +``` + +This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work. diff --git a/api/swagger.yaml b/api/swagger.yaml new file mode 100644 index 0000000..e437bae --- /dev/null +++ b/api/swagger.yaml @@ -0,0 +1,3682 @@ +--- +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" +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: + - name: "handle" + in: "query" + description: "Filter by user handle" + required: false + type: "string" + - name: "isAvailable" + in: "query" + description: "Filter by user availability" + required: false + type: "boolean" + - name: "groupId" + in: "query" + description: "Filter by user group Id" + required: false + type: "string" + format: "UUID" + - name: "roleId" + in: "query" + description: "Filter by user roleId" + 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: "isAvailable" + in: "query" + description: "Filter by user availability" + required: false + type: "boolean" + - name: "groupId" + in: "query" + description: "Filter by user group Id" + required: false + type: "string" + format: "UUID" + - name: "roleId" + in: "query" + description: "Filter by user roleId" + 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" + 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" + 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: + - 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: [] + 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: [] + 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: + - 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: + - 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: + - 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: + - 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: + - 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: + - 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: + - 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: + - 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 users 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: + - 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: + - 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" +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" + - "id" + - "isAvailable" + properties: + id: + type: "string" + format: "UUID" + description: "The id of the user." + handle: + type: "string" + description: "The handle of the user." + isAvailable: + type: "boolean" + description: "Indicates the availability 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." + - $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." + isAvailable: + type: "boolean" + description: "Indicates the availability of the user." + description: "Properties that are provided when creating or editing a User.\n" + example: + isAvailable: true + handle: "handle" + UserUpdateRequestBody: + type: "object" + properties: + handle: + type: "string" + description: "The handle of the user." + isAvailable: + type: "boolean" + description: "Indicates the availability of the user." + example: + isAvailable: true + handle: "handle" + 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/controllers/Achievements.js b/controllers/Achievements.js new file mode 100644 index 0000000..cccd0cb --- /dev/null +++ b/controllers/Achievements.js @@ -0,0 +1,89 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Achievements = require('../service/AchievementsService'); + +module.exports.usersUserIdAchievementsAchievementsProviderIdDELETE = function usersUserIdAchievementsAchievementsProviderIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; + Achievements.usersUserIdAchievementsAchievementsProviderIdDELETE(userId,achievementsProviderId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsAchievementsProviderIdGET = function usersUserIdAchievementsAchievementsProviderIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; + Achievements.usersUserIdAchievementsAchievementsProviderIdGET(userId,achievementsProviderId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsAchievementsProviderIdHEAD = function usersUserIdAchievementsAchievementsProviderIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; + Achievements.usersUserIdAchievementsAchievementsProviderIdHEAD(userId,achievementsProviderId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsAchievementsProviderIdPATCH = function usersUserIdAchievementsAchievementsProviderIdPATCH (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; + var body = req.swagger.params['body'].value; + Achievements.usersUserIdAchievementsAchievementsProviderIdPATCH(userId,achievementsProviderId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsGET = function usersUserIdAchievementsGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsproviderName = req.swagger.params['achievementsproviderName'].value; + Achievements.usersUserIdAchievementsGET(userId,achievementsproviderName) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsHEAD = function usersUserIdAchievementsHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var achievementsproviderName = req.swagger.params['achievementsproviderName'].value; + Achievements.usersUserIdAchievementsHEAD(userId,achievementsproviderName) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAchievementsPOST = function usersUserIdAchievementsPOST (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + Achievements.usersUserIdAchievementsPOST(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/AchievementsProvider.js b/controllers/AchievementsProvider.js new file mode 100644 index 0000000..e1248c9 --- /dev/null +++ b/controllers/AchievementsProvider.js @@ -0,0 +1,82 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var AchievementsProvider = require('../service/AchievementsProviderService'); + +module.exports.achievementsProvidersGET = function achievementsProvidersGET (req, res, next) { + var name = req.swagger.params['name'].value; + AchievementsProvider.achievementsProvidersGET(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersHEAD = function achievementsProvidersHEAD (req, res, next) { + var name = req.swagger.params['name'].value; + AchievementsProvider.achievementsProvidersHEAD(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersPOST = function achievementsProvidersPOST (req, res, next) { + var body = req.swagger.params['body'].value; + AchievementsProvider.achievementsProvidersPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersProviderIdDELETE = function achievementsProvidersProviderIdDELETE (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + AchievementsProvider.achievementsProvidersProviderIdDELETE(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersProviderIdGET = function achievementsProvidersProviderIdGET (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + AchievementsProvider.achievementsProvidersProviderIdGET(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersProviderIdHEAD = function achievementsProvidersProviderIdHEAD (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + AchievementsProvider.achievementsProvidersProviderIdHEAD(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.achievementsProvidersProviderIdPATCH = function achievementsProvidersProviderIdPATCH (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + var body = req.swagger.params['body'].value; + AchievementsProvider.achievementsProvidersProviderIdPATCH(providerId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/AttributeGroups.js b/controllers/AttributeGroups.js new file mode 100644 index 0000000..3d2cd95 --- /dev/null +++ b/controllers/AttributeGroups.js @@ -0,0 +1,84 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var AttributeGroups = require('../service/AttributeGroupsService'); + +module.exports.attributeGroupsGET = function attributeGroupsGET (req, res, next) { + var name = req.swagger.params['name'].value; + var organizationId = req.swagger.params['organizationId'].value; + AttributeGroups.attributeGroupsGET(name,organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsHEAD = function attributeGroupsHEAD (req, res, next) { + var name = req.swagger.params['name'].value; + var organizationId = req.swagger.params['organizationId'].value; + AttributeGroups.attributeGroupsHEAD(name,organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsIdDELETE = function attributeGroupsIdDELETE (req, res, next) { + var id = req.swagger.params['id'].value; + AttributeGroups.attributeGroupsIdDELETE(id) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsIdGET = function attributeGroupsIdGET (req, res, next) { + var id = req.swagger.params['id'].value; + AttributeGroups.attributeGroupsIdGET(id) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsIdHEAD = function attributeGroupsIdHEAD (req, res, next) { + var id = req.swagger.params['id'].value; + AttributeGroups.attributeGroupsIdHEAD(id) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsIdPATCH = function attributeGroupsIdPATCH (req, res, next) { + var id = req.swagger.params['id'].value; + var body = req.swagger.params['body'].value; + AttributeGroups.attributeGroupsIdPATCH(id,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributeGroupsPOST = function attributeGroupsPOST (req, res, next) { + var body = req.swagger.params['body'].value; + AttributeGroups.attributeGroupsPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/Attributes.js b/controllers/Attributes.js new file mode 100644 index 0000000..9bca5cf --- /dev/null +++ b/controllers/Attributes.js @@ -0,0 +1,82 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Attributes = require('../service/AttributesService'); + +module.exports.attributesAttributeIdDELETE = function attributesAttributeIdDELETE (req, res, next) { + var attributeId = req.swagger.params['attributeId'].value; + Attributes.attributesAttributeIdDELETE(attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesAttributeIdGET = function attributesAttributeIdGET (req, res, next) { + var attributeId = req.swagger.params['attributeId'].value; + Attributes.attributesAttributeIdGET(attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesAttributeIdHEAD = function attributesAttributeIdHEAD (req, res, next) { + var attributeId = req.swagger.params['attributeId'].value; + Attributes.attributesAttributeIdHEAD(attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesAttributeIdPATCH = function attributesAttributeIdPATCH (req, res, next) { + var attributeId = req.swagger.params['attributeId'].value; + var body = req.swagger.params['body'].value; + Attributes.attributesAttributeIdPATCH(attributeId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesGET = function attributesGET (req, res, next) { + var attributeGroupId = req.swagger.params['attributeGroupId'].value; + var name = req.swagger.params['name'].value; + Attributes.attributesGET(attributeGroupId,name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesHEAD = function attributesHEAD (req, res, next) { + Attributes.attributesHEAD() + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.attributesPOST = function attributesPOST (req, res, next) { + var body = req.swagger.params['body'].value; + Attributes.attributesPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/ExternalProfiles.js b/controllers/ExternalProfiles.js new file mode 100644 index 0000000..0b8c907 --- /dev/null +++ b/controllers/ExternalProfiles.js @@ -0,0 +1,88 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var ExternalProfiles = require('../service/ExternalProfilesService'); + +module.exports.usersUserIdExternalProfilesGET = function usersUserIdExternalProfilesGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var organizationName = req.swagger.params['organizationName'].value; + ExternalProfiles.usersUserIdExternalProfilesGET(userId,organizationName) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesHEAD = function usersUserIdExternalProfilesHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + ExternalProfiles.usersUserIdExternalProfilesHEAD(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesOrganizationIdDELETE = function usersUserIdExternalProfilesOrganizationIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + var organizationId = req.swagger.params['organizationId'].value; + ExternalProfiles.usersUserIdExternalProfilesOrganizationIdDELETE(userId,organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesOrganizationIdGET = function usersUserIdExternalProfilesOrganizationIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var organizationId = req.swagger.params['organizationId'].value; + ExternalProfiles.usersUserIdExternalProfilesOrganizationIdGET(userId,organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesOrganizationIdHEAD = function usersUserIdExternalProfilesOrganizationIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var organizationId = req.swagger.params['organizationId'].value; + ExternalProfiles.usersUserIdExternalProfilesOrganizationIdHEAD(userId,organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesOrganizationIdPATCH = function usersUserIdExternalProfilesOrganizationIdPATCH (req, res, next) { + var userId = req.swagger.params['userId'].value; + var organizationId = req.swagger.params['organizationId'].value; + var body = req.swagger.params['body'].value; + ExternalProfiles.usersUserIdExternalProfilesOrganizationIdPATCH(userId,organizationId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdExternalProfilesPOST = function usersUserIdExternalProfilesPOST (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + ExternalProfiles.usersUserIdExternalProfilesPOST(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/Organizations.js b/controllers/Organizations.js new file mode 100644 index 0000000..2e79628 --- /dev/null +++ b/controllers/Organizations.js @@ -0,0 +1,82 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Organizations = require('../service/OrganizationsService'); + +module.exports.organizationsGET = function organizationsGET (req, res, next) { + var name = req.swagger.params['name'].value; + Organizations.organizationsGET(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsHEAD = function organizationsHEAD (req, res, next) { + var name = req.swagger.params['name'].value; + Organizations.organizationsHEAD(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsOrganizationIdDELETE = function organizationsOrganizationIdDELETE (req, res, next) { + var organizationId = req.swagger.params['organizationId'].value; + Organizations.organizationsOrganizationIdDELETE(organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsOrganizationIdGET = function organizationsOrganizationIdGET (req, res, next) { + var organizationId = req.swagger.params['organizationId'].value; + Organizations.organizationsOrganizationIdGET(organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsOrganizationIdHEAD = function organizationsOrganizationIdHEAD (req, res, next) { + var organizationId = req.swagger.params['organizationId'].value; + Organizations.organizationsOrganizationIdHEAD(organizationId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsOrganizationIdPATCH = function organizationsOrganizationIdPATCH (req, res, next) { + var organizationId = req.swagger.params['organizationId'].value; + var body = req.swagger.params['body'].value; + Organizations.organizationsOrganizationIdPATCH(organizationId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.organizationsPOST = function organizationsPOST (req, res, next) { + var body = req.swagger.params['body'].value; + Organizations.organizationsPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/Roles.js b/controllers/Roles.js new file mode 100644 index 0000000..83e4447 --- /dev/null +++ b/controllers/Roles.js @@ -0,0 +1,82 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Roles = require('../service/RolesService'); + +module.exports.rolesGET = function rolesGET (req, res, next) { + var name = req.swagger.params['name'].value; + Roles.rolesGET(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesHEAD = function rolesHEAD (req, res, next) { + var name = req.swagger.params['name'].value; + Roles.rolesHEAD(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesPOST = function rolesPOST (req, res, next) { + var body = req.swagger.params['body'].value; + Roles.rolesPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesRoleIdDELETE = function rolesRoleIdDELETE (req, res, next) { + var roleId = req.swagger.params['roleId'].value; + Roles.rolesRoleIdDELETE(roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesRoleIdGET = function rolesRoleIdGET (req, res, next) { + var roleId = req.swagger.params['roleId'].value; + Roles.rolesRoleIdGET(roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesRoleIdHEAD = function rolesRoleIdHEAD (req, res, next) { + var roleId = req.swagger.params['roleId'].value; + Roles.rolesRoleIdHEAD(roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.rolesRoleIdPATCH = function rolesRoleIdPATCH (req, res, next) { + var roleId = req.swagger.params['roleId'].value; + var body = req.swagger.params['body'].value; + Roles.rolesRoleIdPATCH(roleId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/Skills.js b/controllers/Skills.js new file mode 100644 index 0000000..76d2680 --- /dev/null +++ b/controllers/Skills.js @@ -0,0 +1,80 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Skills = require('../service/SkillsService'); + +module.exports.skillsGET = function skillsGET (req, res, next) { + Skills.skillsGET() + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsHEAD = function skillsHEAD (req, res, next) { + Skills.skillsHEAD() + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsPOST = function skillsPOST (req, res, next) { + var body = req.swagger.params['body'].value; + Skills.skillsPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsSkillIdDELETE = function skillsSkillIdDELETE (req, res, next) { + var skillId = req.swagger.params['skillId'].value; + Skills.skillsSkillIdDELETE(skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsSkillIdGET = function skillsSkillIdGET (req, res, next) { + var skillId = req.swagger.params['skillId'].value; + Skills.skillsSkillIdGET(skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsSkillIdHEAD = function skillsSkillIdHEAD (req, res, next) { + var skillId = req.swagger.params['skillId'].value; + Skills.skillsSkillIdHEAD(skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsSkillIdPATCH = function skillsSkillIdPATCH (req, res, next) { + var skillId = req.swagger.params['skillId'].value; + var body = req.swagger.params['body'].value; + Skills.skillsSkillIdPATCH(skillId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/SkillsProvider.js b/controllers/SkillsProvider.js new file mode 100644 index 0000000..642a12d --- /dev/null +++ b/controllers/SkillsProvider.js @@ -0,0 +1,82 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var SkillsProvider = require('../service/SkillsProviderService'); + +module.exports.skillsProvidersGET = function skillsProvidersGET (req, res, next) { + var name = req.swagger.params['name'].value; + SkillsProvider.skillsProvidersGET(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersHEAD = function skillsProvidersHEAD (req, res, next) { + var name = req.swagger.params['name'].value; + SkillsProvider.skillsProvidersHEAD(name) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersPOST = function skillsProvidersPOST (req, res, next) { + var body = req.swagger.params['body'].value; + SkillsProvider.skillsProvidersPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersProviderIdDELETE = function skillsProvidersProviderIdDELETE (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + SkillsProvider.skillsProvidersProviderIdDELETE(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersProviderIdGET = function skillsProvidersProviderIdGET (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + SkillsProvider.skillsProvidersProviderIdGET(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersProviderIdHEAD = function skillsProvidersProviderIdHEAD (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + SkillsProvider.skillsProvidersProviderIdHEAD(providerId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.skillsProvidersProviderIdPATCH = function skillsProvidersProviderIdPATCH (req, res, next) { + var providerId = req.swagger.params['providerId'].value; + var body = req.swagger.params['body'].value; + SkillsProvider.skillsProvidersProviderIdPATCH(providerId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/UserAttributes.js b/controllers/UserAttributes.js new file mode 100644 index 0000000..8add563 --- /dev/null +++ b/controllers/UserAttributes.js @@ -0,0 +1,93 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var UserAttributes = require('../service/UserAttributesService'); + +module.exports.usersUserIdAttributesAttributeIdDELETE = function usersUserIdAttributesAttributeIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeId = req.swagger.params['attributeId'].value; + UserAttributes.usersUserIdAttributesAttributeIdDELETE(userId,attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesAttributeIdGET = function usersUserIdAttributesAttributeIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeId = req.swagger.params['attributeId'].value; + UserAttributes.usersUserIdAttributesAttributeIdGET(userId,attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesAttributeIdHEAD = function usersUserIdAttributesAttributeIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeId = req.swagger.params['attributeId'].value; + UserAttributes.usersUserIdAttributesAttributeIdHEAD(userId,attributeId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesAttributeIdPATCH = function usersUserIdAttributesAttributeIdPATCH (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeId = req.swagger.params['attributeId'].value; + var body = req.swagger.params['body'].value; + UserAttributes.usersUserIdAttributesAttributeIdPATCH(userId,attributeId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesGET = function usersUserIdAttributesGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeName = req.swagger.params['attributeName'].value; + var attributeGroupName = req.swagger.params['attributeGroupName'].value; + var attributeGroupId = req.swagger.params['attributeGroupId'].value; + UserAttributes.usersUserIdAttributesGET(userId,attributeName,attributeGroupName,attributeGroupId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesHEAD = function usersUserIdAttributesHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var attributeName = req.swagger.params['attributeName'].value; + var attributeGroupName = req.swagger.params['attributeGroupName'].value; + var attributeGroupId = req.swagger.params['attributeGroupId'].value; + UserAttributes.usersUserIdAttributesHEAD(userId,attributeName,attributeGroupName,attributeGroupId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdAttributesPOST = function usersUserIdAttributesPOST (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + UserAttributes.usersUserIdAttributesPOST(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/UserRoles.js b/controllers/UserRoles.js new file mode 100644 index 0000000..e9e3f80 --- /dev/null +++ b/controllers/UserRoles.js @@ -0,0 +1,74 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var UserRoles = require('../service/UserRolesService'); + +module.exports.usersUserIdRolesGET = function usersUserIdRolesGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + UserRoles.usersUserIdRolesGET(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdRolesHEAD = function usersUserIdRolesHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + UserRoles.usersUserIdRolesHEAD(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdRolesPOST = function usersUserIdRolesPOST (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + UserRoles.usersUserIdRolesPOST(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdRolesRoleIdDELETE = function usersUserIdRolesRoleIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + var roleId = req.swagger.params['roleId'].value; + UserRoles.usersUserIdRolesRoleIdDELETE(userId,roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdRolesRoleIdGET = function usersUserIdRolesRoleIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var roleId = req.swagger.params['roleId'].value; + UserRoles.usersUserIdRolesRoleIdGET(userId,roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdRolesRoleIdHEAD = function usersUserIdRolesRoleIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var roleId = req.swagger.params['roleId'].value; + UserRoles.usersUserIdRolesRoleIdHEAD(userId,roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/Users.js b/controllers/Users.js new file mode 100644 index 0000000..50e9abe --- /dev/null +++ b/controllers/Users.js @@ -0,0 +1,88 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var Users = require('../service/UsersService'); + +module.exports.usersGET = function usersGET (req, res, next) { + var handle = req.swagger.params['handle'].value; + var isAvailable = req.swagger.params['isAvailable'].value; + var groupId = req.swagger.params['groupId'].value; + var roleId = req.swagger.params['roleId'].value; + Users.usersGET(handle,isAvailable,groupId,roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersHEAD = function usersHEAD (req, res, next) { + var handle = req.swagger.params['handle'].value; + var isAvailable = req.swagger.params['isAvailable'].value; + var groupId = req.swagger.params['groupId'].value; + var roleId = req.swagger.params['roleId'].value; + Users.usersHEAD(handle,isAvailable,groupId,roleId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersPOST = function usersPOST (req, res, next) { + var body = req.swagger.params['body'].value; + Users.usersPOST(body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdDELETE = function usersUserIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + Users.usersUserIdDELETE(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdGET = function usersUserIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + Users.usersUserIdGET(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdHEAD = function usersUserIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + Users.usersUserIdHEAD(userId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdPATCH = function usersUserIdPATCH (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + Users.usersUserIdPATCH(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/controllers/UsersSkills.js b/controllers/UsersSkills.js new file mode 100644 index 0000000..466df91 --- /dev/null +++ b/controllers/UsersSkills.js @@ -0,0 +1,89 @@ +'use strict'; + +var utils = require('../utils/writer.js'); +var UsersSkills = require('../service/UsersSkillsService'); + +module.exports.usersUserIdSkillsGET = function usersUserIdSkillsGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillName = req.swagger.params['skillName'].value; + UsersSkills.usersUserIdSkillsGET(userId,skillName) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsHEAD = function usersUserIdSkillsHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillName = req.swagger.params['skillName'].value; + UsersSkills.usersUserIdSkillsHEAD(userId,skillName) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsPOST = function usersUserIdSkillsPOST (req, res, next) { + var userId = req.swagger.params['userId'].value; + var body = req.swagger.params['body'].value; + UsersSkills.usersUserIdSkillsPOST(userId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsSkillIdDELETE = function usersUserIdSkillsSkillIdDELETE (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillId = req.swagger.params['skillId'].value; + UsersSkills.usersUserIdSkillsSkillIdDELETE(userId,skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsSkillIdGET = function usersUserIdSkillsSkillIdGET (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillId = req.swagger.params['skillId'].value; + UsersSkills.usersUserIdSkillsSkillIdGET(userId,skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsSkillIdHEAD = function usersUserIdSkillsSkillIdHEAD (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillId = req.swagger.params['skillId'].value; + UsersSkills.usersUserIdSkillsSkillIdHEAD(userId,skillId) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; + +module.exports.usersUserIdSkillsSkillIdPATCH = function usersUserIdSkillsSkillIdPATCH (req, res, next) { + var userId = req.swagger.params['userId'].value; + var skillId = req.swagger.params['skillId'].value; + var body = req.swagger.params['body'].value; + UsersSkills.usersUserIdSkillsSkillIdPATCH(userId,skillId,body) + .then(function (response) { + utils.writeJson(res, response); + }) + .catch(function (response) { + utils.writeJson(res, response); + }); +}; diff --git a/docs/UBahn_API_Swagger_Specification.yaml b/docs/UBahn_API_Swagger_Specification.yaml new file mode 100644 index 0000000..c6a15b1 --- /dev/null +++ b/docs/UBahn_API_Swagger_Specification.yaml @@ -0,0 +1,3717 @@ +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' + +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' + +securityDefinitions: + Bearer: + type: apiKey + name: Authorization + in: header + +produces: + - application/json +consumes: + - 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.' + security: + - Bearer: [] + parameters: + - name: handle + type: string + description: Filter by user handle + in: query + required: false + - name: isAvailable + type: boolean + description: Filter by user availability + in: query + required: false + - name: groupId + type: string + format: UUID + description: Filter by user group Id + in: query + required: false + - name: roleId + type: string + format: UUID + description: Filter by user roleId + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on users in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: handle + type: string + description: Filter by user handle + in: query + required: false + - name: isAvailable + type: boolean + description: Filter by user availability + in: query + required: false + - name: groupId + type: string + format: UUID + description: Filter by user group Id + in: query + required: false + - name: roleId + type: string + format: UUID + description: Filter by user roleId + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new User. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /users/{userId}: + get: + description: | + Get User with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + head: + description: | + Get User with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing User with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing User with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - Users + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + + /users/{userId}/skills: + get: + description: | + Filter skills by its name given an user id. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillName + type: string + description: Filter by skill name (through skill id) + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on users skills in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillName + type: string + description: Filter by skill name (through skill id) + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new User Skill. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + + /users/{userId}/skills/{skillId}: + get: + description: | + Get User Skills with given user and skill id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + 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' + head: + description: | + Get User Skills with given ids, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing skill with given ids. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing User Skill with given ids. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'Users Skills' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + 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' + + /skills: + get: + description: | + Get list of skills in the application. If no results, then empty array is returned. Multiple filters are + supported. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills' + security: + - Bearer: [] + 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' + head: + description: | + Retrieve header information for get operation on Skills in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills' + security: + - Bearer: [] + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Skill. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Skills' + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /skills/{skillId}: + get: + description: | + Get Skill by given skill id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills' + security: + - Bearer: [] + parameters: + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + 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' + head: + description: | + Retrieve header information for get operation on Skill by its Id in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills' + security: + - Bearer: [] + parameters: + - name: skillId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing skill with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - Skills + security: + - Bearer: [] + parameters: + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing skill with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - Skills + security: + - Bearer: [] + parameters: + - name: skillId + type: string + description: The skill id + format: UUID + in: path + required: true + 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' + + /skillsProviders: + get: + description: | + Search Skills Provider in the application. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by provider name + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on skills providers in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by provider name + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Skills Provider. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /skillsProviders/{providerId}: + get: + description: | + Get skills provider with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + head: + description: | + Get skills provider with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing skills provider with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing skills provider with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'Skills Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + + /roles: + get: + description: | + Search Roles in the application. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by role name + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on Roles in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by role name + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Role. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /roles/{roleId}: + get: + description: | + Get role with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + 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' + head: + description: | + Get role with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing role with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing role with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - Roles + security: + - Bearer: [] + parameters: + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + 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' + + /users/{userId}/roles: + get: + description: | + Get User Roles that belong to given user id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + head: + description: | + Get User Roles that belong to given user id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + post: + description: | + Create a new User Role. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + + /users/{userId}/roles/{roleId}: + get: + description: | + Get role by its id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + 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' + head: + description: | + Retrieve header information for a search operation on User Roles in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + delete: + description: | + Remove an existing user role with given user and role id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'User Roles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: roleId + type: string + description: The role id + format: UUID + in: path + required: true + 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' + + /users/{userId}/externalProfiles: + get: + description: | + Get External Profiles with given user id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: organizationName + type: string + description: The organization name + in: query + required: false + 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' + head: + description: | + Get External Profiles with given user id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + 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' + post: + description: | + Create a new External Profile for given user id. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + + /users/{userId}/externalProfiles/{organizationId}: + get: + description: | + Get external profile with given user id and organization id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + head: + description: | + Get external profile with given user id and organization id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing external profile with given user id and organization id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing external profile with given user id and organization id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'External Profiles' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + + /users/{userId}/achievements: + get: + description: | + Get Achievements for given user id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsproviderName + type: string + description: The achievement provider name + in: query + required: false + 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' + head: + description: | + Get Achievements for given user id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsproviderName + type: string + description: The achievement provider name + in: query + required: false + 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' + post: + description: | + Create a new Achievement. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + + /users/{userId}/achievements/{achievementsProviderId}: + get: + description: | + Get Achievements for given user id and provider id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsProviderId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + head: + description: | + Get Achievements for given user id and provider id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsProviderId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing Achievement with given userId and achievement providerId. Only the fields in the request body are updated. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsProviderId + type: string + description: The provider id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing Achievement with given userId and achievement providerId. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'Achievements' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: achievementsProviderId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + + /achievementsProviders: + get: + description: | + Search Achievements Provider in the application. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by provider name + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on achivements provider in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by provider name + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Achievements Provider. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /achievementsProviders/{providerId}: + get: + description: | + Get achievements provider with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + head: + description: | + Get achivements provider with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing achivements provider with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing achiements provider with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'Achievements Provider' + security: + - Bearer: [] + parameters: + - name: providerId + type: string + description: The provider id + format: UUID + in: path + required: true + 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' + + /organizations: + get: + description: | + Search organizations in the application. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by organization name + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on organizations in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by organization name + in: query + required: false + responses: + 200: + description: success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Organization. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /organizations/{organizationId}: + get: + description: | + Get organization with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + head: + description: | + Get organization with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing organization with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing organization with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - Organizations + security: + - Bearer: [] + parameters: + - name: organizationId + type: string + description: The organization id + format: UUID + in: path + required: true + 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' + + /users/{userId}/attributes: + get: + description: | + Get attributes for the given user. + Optionally, 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. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeName + type: string + description: Filter by the attribute name + in: query + required: false + - name: attributeGroupName + type: string + description: Filter by the attribute group name + in: query + required: false + - name: attributeGroupId + type: string + description: Filter by the attribute group id + in: query + format: UUID + required: false + 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' + head: + description: | + Retrieve header information for a search operation on users attributes in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeName + type: string + description: Filter by the attribute name + in: query + required: false + - name: attributeGroupName + type: string + description: Filter by the attribute group name + in: query + required: false + - name: attributeGroupId + type: string + description: Filter by the attribute group id + in: query + format: UUID + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new User Attribute. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: body + in: 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' + + /users/{userId}/attributes/{attributeId}: + get: + description: | + Get User Attributes with given user and attribute id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + head: + description: | + Get User Attributes with given ids, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing user attribute with given ids. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing User Attribute with given ids. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'User Attributes' + security: + - Bearer: [] + parameters: + - name: userId + type: string + description: The user id + format: UUID + in: path + required: true + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + + /attributes: + get: + description: | + Get list of attributes in the application. If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attributes' + security: + - Bearer: [] + parameters: + - name: attributeGroupId + type: string + format: UUID + description: Filter by attribute group id + required: false + in: query + - name: name + type: string + description: Filter by attribute name + in: query + required: false + 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' + head: + description: | + Retrieve header information for get operation on Attributes in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attributes' + security: + - Bearer: [] + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Attribute. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Attributes' + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /attributes/{attributeId}: + get: + description: | + Get Attribute by given attribute id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attributes' + security: + - Bearer: [] + parameters: + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + head: + description: | + Retrieve header information for get operation on Attribute by its id in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - Attributes + security: + - Bearer: [] + parameters: + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing attribute with given id. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - Attributes + security: + - Bearer: [] + parameters: + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing attribute with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - Attributes + security: + - Bearer: [] + parameters: + - name: attributeId + type: string + description: The attribute id + format: UUID + in: path + required: true + 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' + + /attributeGroups: + get: + description: | + Search Attribute Groups in the application. Multiple filters are supported. + If no results, then empty array is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by group name + in: query + required: false + - name: organizationId + type: string + format: UUID + description: Filter by organization id + in: query + required: false + 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' + head: + description: | + Retrieve header information for a search operation on Attribute Groups in the application. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: name + type: string + description: Filter by group name + in: query + required: false + - name: organizationId + type: string + format: UUID + description: Filter by organization id + in: query + required: false + responses: + 200: + description: Success response + 400: + $ref: '#/definitions/BadRequest' + 401: + $ref: '#/definitions/Unauthorized' + 403: + $ref: '#/definitions/Forbidden' + 500: + $ref: '#/definitions/ServerError' + post: + description: | + Create a new Attribute Group. + + **Security** - This endpoint is accessible by all authenticated users. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: body + in: 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' + + /attributeGroups/{id}: + get: + description: | + Get Attribute Groups with given id. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: id + type: string + description: The id + format: UUID + in: path + required: true + 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' + head: + description: | + Get Attribute Group with given id, but only header information is returned. + + **Security** - Note that for non-admin users, this endpoint will only return entities that + the user has created. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: id + type: string + description: The id + format: UUID + in: path + required: true + 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' + patch: + description: | + Update an existing Attribute Group with given id. Only the fields in the request body are updated. + + **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the + calling user has created. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: id + type: string + description: The id + format: UUID + in: path + required: true + - name: body + in: 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' + delete: + description: | + Remove an existing Attribute Group with given id. + + **Security** - Note that this endpoint is only available for admin users. + tags: + - 'Attribute Groups' + security: + - Bearer: [] + parameters: + - name: id + type: string + description: The id + format: UUID + in: path + required: true + 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' + +definitions: + AuditFields: + type: object + description: Describes the audit fields that are present in all the models in this API. + required: + - created + - updated + - createdBy + - 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. + + Achievement: + allOf: + - type: object + required: + - userId + - achievementsProviderId + - name + - uri + - certifierId + - certifierDate + 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: + required: + - achievementsProviderId + - name + - uri + - certifierId + - certifierDate + 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. + + 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: + - id + - skillProviderId + - name + - externalId + - 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: + required: + - skillProviderId + - name + - externalId + - uri + 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 + + 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: + - userId + - organizationId + - uri + 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. + + ExternalProfileUpdateRequestBody: + type: object + properties: + uri: + type: string + description: The uri of the external profile. + + 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 + - roleId + properties: + userId: + type: string + format: UUID + description: The user to be associated with the role + - $ref: '#/definitions/UserRoleRequestBody' + - $ref: '#/definitions/AuditFields' + + UserRoleRequestBody: + type: object + description: | + Represents a Role that belongs to a given user. + required: + - roleId + properties: + roleId: + type: string + format: UUID + description: The roleId of this user role. + + User: + allOf: + - type: object + required: + - id + - handle + - isAvailable + properties: + id: + type: string + format: UUID + description: The id of the user. + handle: + type: string + description: The handle of the user. + isAvailable: + type: boolean + description: Indicates the availability of the user. + - $ref: '#/definitions/AuditFields' + + UserSkill: + allOf: + - type: object + required: + - userId + - skillId + - metricValue + - certifierId + - certifiedDate + 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: + required: + - skillId + - metricValue + - certifierId + - certifiedDate + allOf: + - type: object + properties: + skillId: + type: string + format: UUID + description: The Skill id. + - $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. + + UserAttribute: + allOf: + - type: object + required: + - userId + - attributeId + - 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: + required: + - attributeId + - value + 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. + + UserRequestBody: + type: object + description: | + Properties that are provided when creating or editing a User. + properties: + handle: + type: string + description: The handle of the user. + isAvailable: + type: boolean + description: Indicates the availability of the user. + + UserUpdateRequestBody: + type: object + properties: + handle: + type: string + description: The handle of the user. + isAvailable: + type: boolean + description: Indicates the availability of the user. + + 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: + - organizationId + - name + description: Request body containing the fields for an Attribute Group. + 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. + + 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 + description: Simple request body containing the name of the entity. + required: + - name + properties: + name: + type: string + description: Name of the entity + + Attribute: + allOf: + - type: object + required: + - id + - attributeGroupId + - 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: + required: + - attributeGroupId + - name + 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 + + # Schema for error body + Unauthorized: + type: object + description: The unauthorized error entity. + properties: + message: + type: string + description: The unauthorized error message. + example: Unable to authenticate the user. + + NotFound: + type: object + description: The not found error entity. + properties: + message: + type: string + description: The not found error message. + example: A resource with the name could not be found. + + ServerError: + type: object + description: The server error entity. + properties: + message: + type: string + description: The server error message. + 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. + + BadRequest: + type: object + description: The bad request error entity. + properties: + message: + type: string + description: The bad request error message. + example: Invalid input. + + Forbidden: + type: object + description: The permission error entity. + properties: + message: + type: string + description: The forbidden error message. + example: You are not allowed to access the request. + + Conflict: + type: object + description: The conflict error entity. + required: + - message + properties: + message: + type: string + description: The conflict error message. + example: Creating a resource with a name already exists. diff --git a/index.js b/index.js new file mode 100644 index 0000000..58fb30b --- /dev/null +++ b/index.js @@ -0,0 +1,44 @@ +'use strict'; + +var fs = require('fs'), + path = require('path'), + http = require('http'); + +var app = require('connect')(); +var swaggerTools = require('swagger-tools'); +var jsyaml = require('js-yaml'); +var serverPort = process.env.PORT; + +// swaggerRouter configuration +var options = { + swaggerUi: path.join(__dirname, '/swagger.json'), + controllers: path.join(__dirname, './controllers'), + useStubs: process.env.NODE_ENV === 'development' // Conditionally turn on stubs (mock mode) +}; + +// The Swagger document (require it, build it programmatically, fetch it from a URL, ...) +var spec = fs.readFileSync(path.join(__dirname,'api/swagger.yaml'), 'utf8'); +var swaggerDoc = jsyaml.safeLoad(spec); + +// Initialize the Swagger middleware +swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) { + + // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain + app.use(middleware.swaggerMetadata()); + + // Validate Swagger requests + app.use(middleware.swaggerValidator()); + + // Route validated requests to appropriate controller + app.use(middleware.swaggerRouter(options)); + + // Serve the Swagger documents and Swagger UI + app.use(middleware.swaggerUi()); + + // Start the server + http.createServer(app).listen(serverPort, function () { + console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort); + console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort); + }); + +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7d2c72d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1084 @@ +{ + "name": "ubahn-api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + }, + "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" + } + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "body-parser": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.4.tgz", + "integrity": "sha1-CQcAxLoohiqFIO83g5X97l9hwik=", + "requires": { + "bytes": "1.0.0", + "content-type": "~1.0.1", + "debug": "~2.2.0", + "depd": "~1.0.1", + "iconv-lite": "0.4.8", + "on-finished": "~2.2.1", + "qs": "2.4.2", + "raw-body": "~2.0.1", + "type-is": "~1.6.2" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "ee-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", + "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + }, + "on-finished": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", + "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", + "requires": { + "ee-first": "1.1.0" + } + }, + "qs": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", + "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" + } + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "requires": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", + "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" + }, + "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" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "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-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + } + }, + "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==" + }, + "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=" + }, + "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" + } + }, + "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.0.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", + "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "requires": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "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" + } + }, + "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==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "requires": { + "lodash": "^4.17.15" + } + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + } + } + }, + "iconv-lite": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", + "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "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" + } + }, + "json-refs": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz", + "integrity": "sha1-uesB/in16j6Sh48VrqEK04taz4k=", + "requires": { + "commander": "^2.9.0", + "graphlib": "^2.1.1", + "js-yaml": "^3.8.3", + "native-promise-only": "^0.8.1", + "path-loader": "^1.0.2", + "slash": "^1.0.0", + "uri-js": "^3.0.2" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash-compat": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/lodash-compat/-/lodash-compat-3.10.2.tgz", + "integrity": "sha1-xpQBKKnTD46QLNLPmf0Muk7PwYM=" + }, + "lodash._arraypool": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz", + "integrity": "sha1-6I7suS4ruEyQZWEv2VigcZzUf5Q=" + }, + "lodash._basebind": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.4.1.tgz", + "integrity": "sha1-6UC5690nwyfgqNqxtVkWxTQelXU=", + "requires": { + "lodash._basecreate": "~2.4.1", + "lodash._setbinddata": "~2.4.1", + "lodash._slice": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash._baseclone": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-2.4.1.tgz", + "integrity": "sha1-MPgj5X4X43NdODvWK2Czh1Q7QYY=", + "requires": { + "lodash._getarray": "~2.4.1", + "lodash._releasearray": "~2.4.1", + "lodash._slice": "~2.4.1", + "lodash.assign": "~2.4.1", + "lodash.foreach": "~2.4.1", + "lodash.forown": "~2.4.1", + "lodash.isarray": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash._basecreate": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz", + "integrity": "sha1-+Ob1tXip405UEXm1a47uv0oofgg=", + "requires": { + "lodash._isnative": "~2.4.1", + "lodash.isobject": "~2.4.1", + "lodash.noop": "~2.4.1" + } + }, + "lodash._basecreatecallback": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz", + "integrity": "sha1-fQsmdknLKeehOdAQO3wR+uhOSFE=", + "requires": { + "lodash._setbinddata": "~2.4.1", + "lodash.bind": "~2.4.1", + "lodash.identity": "~2.4.1", + "lodash.support": "~2.4.1" + } + }, + "lodash._basecreatewrapper": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz", + "integrity": "sha1-TTHy595+E0+/KAN2K4FQsyUZZm8=", + "requires": { + "lodash._basecreate": "~2.4.1", + "lodash._setbinddata": "~2.4.1", + "lodash._slice": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash._createwrapper": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz", + "integrity": "sha1-UdaVeXPaTtVW43KQ2MGhjFPeFgc=", + "requires": { + "lodash._basebind": "~2.4.1", + "lodash._basecreatewrapper": "~2.4.1", + "lodash._slice": "~2.4.1", + "lodash.isfunction": "~2.4.1" + } + }, + "lodash._getarray": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._getarray/-/lodash._getarray-2.4.1.tgz", + "integrity": "sha1-+vH3+BD6mFolHCGHQESBCUg55e4=", + "requires": { + "lodash._arraypool": "~2.4.1" + } + }, + "lodash._isnative": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", + "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=" + }, + "lodash._maxpoolsize": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._maxpoolsize/-/lodash._maxpoolsize-2.4.1.tgz", + "integrity": "sha1-nUgvRjuOZq++WcLBTtsRcGAXIzQ=" + }, + "lodash._objecttypes": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", + "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=" + }, + "lodash._releasearray": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._releasearray/-/lodash._releasearray-2.4.1.tgz", + "integrity": "sha1-phOWMNdtFTawfdyAliiJsIL2pkE=", + "requires": { + "lodash._arraypool": "~2.4.1", + "lodash._maxpoolsize": "~2.4.1" + } + }, + "lodash._setbinddata": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz", + "integrity": "sha1-98IAzRuS7yNrOZ7s9zxkjReqlNI=", + "requires": { + "lodash._isnative": "~2.4.1", + "lodash.noop": "~2.4.1" + } + }, + "lodash._shimkeys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", + "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash._slice": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash._slice/-/lodash._slice-2.4.1.tgz", + "integrity": "sha1-dFz0GlNZexj2iImFREBe+isG2Q8=" + }, + "lodash.assign": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-2.4.1.tgz", + "integrity": "sha1-hMOVlt1xGBqXsGUpE6fJZ15Jsao=", + "requires": { + "lodash._basecreatecallback": "~2.4.1", + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.bind": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-2.4.1.tgz", + "integrity": "sha1-XRn6AFyMTSNvr0dCx7eh/Kvikmc=", + "requires": { + "lodash._createwrapper": "~2.4.1", + "lodash._slice": "~2.4.1" + } + }, + "lodash.clonedeep": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-2.4.1.tgz", + "integrity": "sha1-8pIDtAsS/uCkXTYxZIJZvrq8eGg=", + "requires": { + "lodash._baseclone": "~2.4.1", + "lodash._basecreatecallback": "~2.4.1" + } + }, + "lodash.foreach": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-2.4.1.tgz", + "integrity": "sha1-/j/Do0yGyUyrb5UiVgKCdB4BYwk=", + "requires": { + "lodash._basecreatecallback": "~2.4.1", + "lodash.forown": "~2.4.1" + } + }, + "lodash.forown": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-2.4.1.tgz", + "integrity": "sha1-eLQer+FAX6lmRZ6kGT/VAtCEUks=", + "requires": { + "lodash._basecreatecallback": "~2.4.1", + "lodash._objecttypes": "~2.4.1", + "lodash.keys": "~2.4.1" + } + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, + "lodash.identity": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-2.4.1.tgz", + "integrity": "sha1-ZpTP+mX++TH3wxzobHRZfPVg9PE=" + }, + "lodash.isarray": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-2.4.1.tgz", + "integrity": "sha1-tSoybB9i9tfac6MdVAHfbvRPD6E=", + "requires": { + "lodash._isnative": "~2.4.1" + } + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.isfunction": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz", + "integrity": "sha1-LP1XXHPkmKtX4xm3f6Aq3vE6lNE=" + }, + "lodash.isobject": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", + "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "requires": { + "lodash._objecttypes": "~2.4.1" + } + }, + "lodash.keys": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", + "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "requires": { + "lodash._isnative": "~2.4.1", + "lodash._shimkeys": "~2.4.1", + "lodash.isobject": "~2.4.1" + } + }, + "lodash.noop": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz", + "integrity": "sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o=" + }, + "lodash.support": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz", + "integrity": "sha1-Mg4LZwMWc8KNeiu12eAzGkUkBRU=", + "requires": { + "lodash._isnative": "~2.4.1" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "multer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", + "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "requires": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.1", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + } + }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "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" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-loader": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.10.tgz", + "integrity": "sha512-CMP0v6S6z8PHeJ6NFVyVJm6WyJjIwFvyz2b0n2/4bKdS/0uZa/9sKUlYZzubrn3zuDRU0zIuEDX9DZYQ2ZI8TA==", + "requires": { + "native-promise-only": "^0.8.1", + "superagent": "^3.8.3" + }, + "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==" + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + }, + "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" + } + } + } + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, + "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==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz", + "integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc=" + }, + "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.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.0.2.tgz", + "integrity": "sha1-osL5jIUxzumcY9jSOLfel7tln8o=", + "requires": { + "bytes": "2.1.0", + "iconv-lite": "0.4.8" + }, + "dependencies": { + "bytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.1.0.tgz", + "integrity": "sha1-rJPEEOL/ycx89LRks4KJBn9eR7Q=" + } + } + }, + "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" + } + }, + "reduce-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", + "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=" + }, + "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==" + }, + "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": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "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==" + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + }, + "spark-md5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-1.0.1.tgz", + "integrity": "sha1-xLmo1Bz3sIRUI6ghgk+N/6D1G3w=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" + }, + "string": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/string/-/string-3.3.3.tgz", + "integrity": "sha1-XqIRzZLSKOGEKUmQpsyXs2anfLA=" + }, + "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" + } + }, + "superagent": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.5.tgz", + "integrity": "sha1-HA3cOvMOgOuE68BcshItqP6UC1U=", + "requires": { + "component-emitter": "~1.2.0", + "cookiejar": "2.0.6", + "debug": "2", + "extend": "3.0.0", + "form-data": "1.0.0-rc3", + "formidable": "~1.0.14", + "methods": "~1.1.1", + "mime": "1.3.4", + "qs": "2.3.3", + "readable-stream": "1.0.27-1", + "reduce-component": "1.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "cookiejar": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz", + "integrity": "sha1-Cr81atANHFohnYjURRgEbdAmrP4=" + }, + "extend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", + "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=" + }, + "form-data": { + "version": "1.0.0-rc3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", + "integrity": "sha1-01vGLn+8KTeuePlIqqDTjZBgdXc=", + "requires": { + "async": "^1.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.3" + } + }, + "formidable": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.16.tgz", + "integrity": "sha1-SRbP38TL7QILJXpqlQWpqzjCzQ4=" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "qs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", + "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=" + }, + "readable-stream": { + "version": "1.0.27-1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", + "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "swagger-converter": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/swagger-converter/-/swagger-converter-0.1.7.tgz", + "integrity": "sha1-oJdRnG8e5N1n4wjZtT3cnCslf5c=", + "requires": { + "lodash.clonedeep": "^2.4.1" + } + }, + "swagger-tools": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/swagger-tools/-/swagger-tools-0.10.1.tgz", + "integrity": "sha1-pFWyV5mOzyDeW06Zvc8acJqw/tw=", + "requires": { + "async": "^1.3.0", + "body-parser": "1.12.4", + "commander": "^2.8.1", + "debug": "^2.2.0", + "js-yaml": "^3.3.1", + "json-refs": "^2.1.5", + "lodash-compat": "^3.10.0", + "multer": "^1.1.0", + "parseurl": "^1.3.0", + "path-to-regexp": "^1.2.0", + "qs": "^4.0.0", + "serve-static": "^1.10.0", + "spark-md5": "^1.0.0", + "string": "^3.3.0", + "superagent": "^1.2.0", + "swagger-converter": "^0.1.7", + "traverse": "^0.6.6", + "z-schema": "^3.15.4" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "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" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "requires": { + "punycode": "^2.1.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=" + }, + "validator": { + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", + "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "z-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.25.1.tgz", + "integrity": "sha512-7tDlwhrBG+oYFdXNOjILSurpfQyuVgkRe3hB2q8TEssamDHB7BbLWYkYO98nTn0FibfdFroFKDjndbgufAgS/Q==", + "requires": { + "commander": "^2.7.1", + "core-js": "^2.5.7", + "lodash.get": "^4.0.0", + "lodash.isequal": "^4.0.0", + "validator": "^10.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..803729f --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "ubahn-api", + "version": "1.0.0", + "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", + "main": "index.js", + "scripts": { + "prestart": "npm install", + "start": "node index.js" + }, + "keywords": [ + "swagger" + ], + "license": "Unlicense", + "private": true, + "dependencies": { + "connect": "^3.2.0", + "js-yaml": "^3.3.0", + "swagger-tools": "0.10.1" + } +} diff --git a/service/AchievementsProviderService.js b/service/AchievementsProviderService.js new file mode 100644 index 0000000..be9dfc8 --- /dev/null +++ b/service/AchievementsProviderService.js @@ -0,0 +1,118 @@ +'use strict'; + + +/** + * Search Achievements Provider in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by provider name (optional) + * returns List + **/ +exports.achievementsProvidersGET = function(name) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on achivements provider in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by provider name (optional) + * no response value expected for this operation + **/ +exports.achievementsProvidersHEAD = function(name) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Achievements Provider. **Security** - This endpoint is accessible by all authenticated users. + * + * body NameRequestBody + * returns AchievementsProvider + **/ +exports.achievementsProvidersPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing achiements provider with given id. **Security** - Note that this endpoint is only available for admin users. + * + * providerId String The provider id + * no response value expected for this operation + **/ +exports.achievementsProvidersProviderIdDELETE = function(providerId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get achievements provider with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * providerId String The provider id + * returns AchievementsProvider + **/ +exports.achievementsProvidersProviderIdGET = function(providerId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get achivements provider with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * providerId String The provider id + * no response value expected for this operation + **/ +exports.achievementsProvidersProviderIdHEAD = function(providerId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing achivements provider with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * providerId String The provider id + * body NameRequestBody + * returns AchievementsProvider + **/ +exports.achievementsProvidersProviderIdPATCH = function(providerId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/AchievementsService.js b/service/AchievementsService.js new file mode 100644 index 0000000..37ce51a --- /dev/null +++ b/service/AchievementsService.js @@ -0,0 +1,125 @@ +'use strict'; + + +/** + * Remove an existing Achievement with given userId and achievement providerId. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * achievementsProviderId String The provider id + * no response value expected for this operation + **/ +exports.usersUserIdAchievementsAchievementsProviderIdDELETE = function(userId,achievementsProviderId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get Achievements for given user id and provider id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * achievementsProviderId String The provider id + * returns List + **/ +exports.usersUserIdAchievementsAchievementsProviderIdGET = function(userId,achievementsProviderId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get Achievements for given user id and provider id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * achievementsProviderId String The provider id + * no response value expected for this operation + **/ +exports.usersUserIdAchievementsAchievementsProviderIdHEAD = function(userId,achievementsProviderId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing Achievement with given userId and achievement providerId. Only the fields in the request body are updated. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * userId String The user id + * achievementsProviderId String The provider id + * body AchievementUpdateRequestBody + * returns Achievement + **/ +exports.usersUserIdAchievementsAchievementsProviderIdPATCH = function(userId,achievementsProviderId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get Achievements for given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * achievementsproviderName String The achievement provider name (optional) + * returns List + **/ +exports.usersUserIdAchievementsGET = function(userId,achievementsproviderName) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get Achievements for given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * achievementsproviderName String The achievement provider name (optional) + * no response value expected for this operation + **/ +exports.usersUserIdAchievementsHEAD = function(userId,achievementsproviderName) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Achievement. **Security** - This endpoint is accessible by all authenticated users. + * + * userId String The user id + * body AchievementRequestBody + * returns Achievement + **/ +exports.usersUserIdAchievementsPOST = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/AttributeGroupsService.js b/service/AttributeGroupsService.js new file mode 100644 index 0000000..badf0f0 --- /dev/null +++ b/service/AttributeGroupsService.js @@ -0,0 +1,120 @@ +'use strict'; + + +/** + * Search Attribute Groups in the application. Multiple filters are supported. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by group name (optional) + * organizationId String Filter by organization id (optional) + * returns List + **/ +exports.attributeGroupsGET = function(name,organizationId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on Attribute Groups in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by group name (optional) + * organizationId String Filter by organization id (optional) + * no response value expected for this operation + **/ +exports.attributeGroupsHEAD = function(name,organizationId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Remove an existing Attribute Group with given id. **Security** - Note that this endpoint is only available for admin users. + * + * id String The id + * no response value expected for this operation + **/ +exports.attributeGroupsIdDELETE = function(id) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get Attribute Groups with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * id String The id + * returns AttributeGroup + **/ +exports.attributeGroupsIdGET = function(id) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get Attribute Group with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * id String The id + * no response value expected for this operation + **/ +exports.attributeGroupsIdHEAD = function(id) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing Attribute Group with given id. Only the fields in the request body are updated. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * id String The id + * body AttributeGroupRequestBody + * returns AttributeGroup + **/ +exports.attributeGroupsIdPATCH = function(id,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Create a new Attribute Group. **Security** - This endpoint is accessible by all authenticated users. + * + * body AttributeGroupRequestBody + * returns AttributeGroup + **/ +exports.attributeGroupsPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/AttributesService.js b/service/AttributesService.js new file mode 100644 index 0000000..41cec61 --- /dev/null +++ b/service/AttributesService.js @@ -0,0 +1,118 @@ +'use strict'; + + +/** + * Remove an existing attribute with given id. **Security** - Note that this endpoint is only available for admin users. + * + * attributeId String The attribute id + * no response value expected for this operation + **/ +exports.attributesAttributeIdDELETE = function(attributeId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get Attribute by given attribute id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * attributeId String The attribute id + * returns Attribute + **/ +exports.attributesAttributeIdGET = function(attributeId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for get operation on Attribute by its id in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * attributeId String The attribute id + * no response value expected for this operation + **/ +exports.attributesAttributeIdHEAD = function(attributeId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing attribute with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * attributeId String The attribute id + * body AttributeUpdateRequestBody + * returns Attribute + **/ +exports.attributesAttributeIdPATCH = function(attributeId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get list of attributes in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * attributeGroupId String Filter by attribute group id (optional) + * name String Filter by attribute name (optional) + * returns List + **/ +exports.attributesGET = function(attributeGroupId,name) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for get operation on Attributes in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * no response value expected for this operation + **/ +exports.attributesHEAD = function() { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Attribute. **Security** - This endpoint is accessible by all authenticated users. + * + * body AttributeRequestBody + * returns Attribute + **/ +exports.attributesPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/ExternalProfilesService.js b/service/ExternalProfilesService.js new file mode 100644 index 0000000..cd6d5d7 --- /dev/null +++ b/service/ExternalProfilesService.js @@ -0,0 +1,124 @@ +'use strict'; + + +/** + * Get External Profiles with given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * organizationName String The organization name (optional) + * returns List + **/ +exports.usersUserIdExternalProfilesGET = function(userId,organizationName) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get External Profiles with given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * no response value expected for this operation + **/ +exports.usersUserIdExternalProfilesHEAD = function(userId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Remove an existing external profile with given user id and organization id. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * organizationId String The organization id + * no response value expected for this operation + **/ +exports.usersUserIdExternalProfilesOrganizationIdDELETE = function(userId,organizationId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get external profile with given user id and organization id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * organizationId String The organization id + * returns ExternalProfile + **/ +exports.usersUserIdExternalProfilesOrganizationIdGET = function(userId,organizationId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get external profile with given user id and organization id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * organizationId String The organization id + * no response value expected for this operation + **/ +exports.usersUserIdExternalProfilesOrganizationIdHEAD = function(userId,organizationId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing external profile with given user id and organization id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * userId String The user id + * organizationId String The organization id + * body ExternalProfileUpdateRequestBody + * returns ExternalProfile + **/ +exports.usersUserIdExternalProfilesOrganizationIdPATCH = function(userId,organizationId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Create a new External Profile for given user id. **Security** - This endpoint is accessible by all authenticated users. + * + * userId String The user id + * body ExternalProfileRequestBody + * returns ExternalProfile + **/ +exports.usersUserIdExternalProfilesPOST = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/OrganizationsService.js b/service/OrganizationsService.js new file mode 100644 index 0000000..6a26c5d --- /dev/null +++ b/service/OrganizationsService.js @@ -0,0 +1,118 @@ +'use strict'; + + +/** + * Search organizations in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by organization name (optional) + * returns List + **/ +exports.organizationsGET = function(name) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on organizations in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by organization name (optional) + * no response value expected for this operation + **/ +exports.organizationsHEAD = function(name) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Remove an existing organization with given id. **Security** - Note that this endpoint is only available for admin users. + * + * organizationId String The organization id + * no response value expected for this operation + **/ +exports.organizationsOrganizationIdDELETE = function(organizationId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get organization with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * organizationId String The organization id + * returns Organization + **/ +exports.organizationsOrganizationIdGET = function(organizationId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get organization with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * organizationId String The organization id + * no response value expected for this operation + **/ +exports.organizationsOrganizationIdHEAD = function(organizationId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing organization with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * organizationId String The organization id + * body NameRequestBody + * returns Organization + **/ +exports.organizationsOrganizationIdPATCH = function(organizationId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Create a new Organization. **Security** - This endpoint is accessible by all authenticated users. + * + * body NameRequestBody + * returns Organization + **/ +exports.organizationsPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/RolesService.js b/service/RolesService.js new file mode 100644 index 0000000..bf9ad3e --- /dev/null +++ b/service/RolesService.js @@ -0,0 +1,118 @@ +'use strict'; + + +/** + * Search Roles in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by role name (optional) + * returns List + **/ +exports.rolesGET = function(name) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on Roles in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by role name (optional) + * no response value expected for this operation + **/ +exports.rolesHEAD = function(name) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Role. **Security** - This endpoint is accessible by all authenticated users. + * + * body NameRequestBody + * returns Role + **/ +exports.rolesPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing role with given id. **Security** - Note that this endpoint is only available for admin users. + * + * roleId String The role id + * no response value expected for this operation + **/ +exports.rolesRoleIdDELETE = function(roleId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get role with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * roleId String The role id + * returns Role + **/ +exports.rolesRoleIdGET = function(roleId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get role with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * roleId String The role id + * no response value expected for this operation + **/ +exports.rolesRoleIdHEAD = function(roleId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing role with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * roleId String The role id + * body NameRequestBody + * returns Role + **/ +exports.rolesRoleIdPATCH = function(roleId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/SkillsProviderService.js b/service/SkillsProviderService.js new file mode 100644 index 0000000..e420a86 --- /dev/null +++ b/service/SkillsProviderService.js @@ -0,0 +1,118 @@ +'use strict'; + + +/** + * Search Skills Provider in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by provider name (optional) + * returns List + **/ +exports.skillsProvidersGET = function(name) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on skills providers in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * name String Filter by provider name (optional) + * no response value expected for this operation + **/ +exports.skillsProvidersHEAD = function(name) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Skills Provider. **Security** - This endpoint is accessible by all authenticated users. + * + * body NameRequestBody + * returns SkillsProvider + **/ +exports.skillsProvidersPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing skills provider with given id. **Security** - Note that this endpoint is only available for admin users. + * + * providerId String The provider id + * no response value expected for this operation + **/ +exports.skillsProvidersProviderIdDELETE = function(providerId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get skills provider with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * providerId String The provider id + * returns SkillsProvider + **/ +exports.skillsProvidersProviderIdGET = function(providerId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get skills provider with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * providerId String The provider id + * no response value expected for this operation + **/ +exports.skillsProvidersProviderIdHEAD = function(providerId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing skills provider with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * providerId String The provider id + * body NameRequestBody + * returns SkillsProvider + **/ +exports.skillsProvidersProviderIdPATCH = function(providerId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/SkillsService.js b/service/SkillsService.js new file mode 100644 index 0000000..d5184e7 --- /dev/null +++ b/service/SkillsService.js @@ -0,0 +1,116 @@ +'use strict'; + + +/** + * Get list of skills in the application. If no results, then empty array is returned. Multiple filters are supported. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * returns List + **/ +exports.skillsGET = function() { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for get operation on Skills in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * no response value expected for this operation + **/ +exports.skillsHEAD = function() { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new Skill. **Security** - This endpoint is accessible by all authenticated users. + * + * body SkillRequestBody + * returns Skill + **/ +exports.skillsPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing skill with given id. **Security** - Note that this endpoint is only available for admin users. + * + * skillId String The skill id + * no response value expected for this operation + **/ +exports.skillsSkillIdDELETE = function(skillId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get Skill by given skill id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * skillId String The skill id + * returns Skill + **/ +exports.skillsSkillIdGET = function(skillId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for get operation on Skill by its Id in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * skillId String The user id + * no response value expected for this operation + **/ +exports.skillsSkillIdHEAD = function(skillId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing skill with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * skillId String The skill id + * body SkillUpdateRequestBody + * returns Skill + **/ +exports.skillsSkillIdPATCH = function(skillId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/UserAttributesService.js b/service/UserAttributesService.js new file mode 100644 index 0000000..bba4518 --- /dev/null +++ b/service/UserAttributesService.js @@ -0,0 +1,129 @@ +'use strict'; + + +/** + * Remove an existing User Attribute with given ids. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * attributeId String The attribute id + * no response value expected for this operation + **/ +exports.usersUserIdAttributesAttributeIdDELETE = function(userId,attributeId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get User Attributes with given user and attribute id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * attributeId String The attribute id + * returns UserAttribute + **/ +exports.usersUserIdAttributesAttributeIdGET = function(userId,attributeId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get User Attributes with given ids, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * attributeId String The attribute id + * no response value expected for this operation + **/ +exports.usersUserIdAttributesAttributeIdHEAD = function(userId,attributeId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing user attribute with given ids. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * userId String The user id + * attributeId String The attribute id + * body UserAttributeUpdateRequestBody + * returns UserAttribute + **/ +exports.usersUserIdAttributesAttributeIdPATCH = function(userId,attributeId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get attributes for the given user. Optionally, 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. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * attributeName String Filter by the attribute name (optional) + * attributeGroupName String Filter by the attribute group name (optional) + * attributeGroupId String Filter by the attribute group id (optional) + * returns List + **/ +exports.usersUserIdAttributesGET = function(userId,attributeName,attributeGroupName,attributeGroupId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on users attributes in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * attributeName String Filter by the attribute name (optional) + * attributeGroupName String Filter by the attribute group name (optional) + * attributeGroupId String Filter by the attribute group id (optional) + * no response value expected for this operation + **/ +exports.usersUserIdAttributesHEAD = function(userId,attributeName,attributeGroupName,attributeGroupId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new User Attribute. **Security** - This endpoint is accessible by all authenticated users. + * + * userId String The user id + * body UserAttributeRequestBody + * returns UserAttribute + **/ +exports.usersUserIdAttributesPOST = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/UserRolesService.js b/service/UserRolesService.js new file mode 100644 index 0000000..da53006 --- /dev/null +++ b/service/UserRolesService.js @@ -0,0 +1,102 @@ +'use strict'; + + +/** + * Get User Roles that belong to given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * returns List + **/ +exports.usersUserIdRolesGET = function(userId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get User Roles that belong to given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * no response value expected for this operation + **/ +exports.usersUserIdRolesHEAD = function(userId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new User Role. **Security** - This endpoint is accessible by all authenticated users. + * + * userId String The user id + * body UserRoleRequestBody + * returns UserRole + **/ +exports.usersUserIdRolesPOST = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing user role with given user and role id. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * roleId String The role id + * no response value expected for this operation + **/ +exports.usersUserIdRolesRoleIdDELETE = function(userId,roleId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get role by its id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * roleId String The role id + * returns List + **/ +exports.usersUserIdRolesRoleIdGET = function(userId,roleId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on User Roles in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * roleId String The role id + * no response value expected for this operation + **/ +exports.usersUserIdRolesRoleIdHEAD = function(userId,roleId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + diff --git a/service/UsersService.js b/service/UsersService.js new file mode 100644 index 0000000..c522706 --- /dev/null +++ b/service/UsersService.js @@ -0,0 +1,124 @@ +'use strict'; + + +/** + * **Point to note** - For non-admin users, this endpoint will only return entities that the user has created. + * + * handle String Filter by user handle (optional) + * isAvailable Boolean Filter by user availability (optional) + * groupId String Filter by user group Id (optional) + * roleId String Filter by user roleId (optional) + * returns List + **/ +exports.usersGET = function(handle,isAvailable,groupId,roleId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on users in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * handle String Filter by user handle (optional) + * isAvailable Boolean Filter by user availability (optional) + * groupId String Filter by user group Id (optional) + * roleId String Filter by user roleId (optional) + * no response value expected for this operation + **/ +exports.usersHEAD = function(handle,isAvailable,groupId,roleId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new User. **Security** - This endpoint is accessible by all authenticated users. + * + * body UserRequestBody + * returns User + **/ +exports.usersPOST = function(body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing User with given id. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * no response value expected for this operation + **/ +exports.usersUserIdDELETE = function(userId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get User with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * returns User + **/ +exports.usersUserIdGET = function(userId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get User with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * no response value expected for this operation + **/ +exports.usersUserIdHEAD = function(userId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing User with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * userId String The user id + * body UserUpdateRequestBody + * returns User + **/ +exports.usersUserIdPATCH = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/service/UsersSkillsService.js b/service/UsersSkillsService.js new file mode 100644 index 0000000..55e1152 --- /dev/null +++ b/service/UsersSkillsService.js @@ -0,0 +1,125 @@ +'use strict'; + + +/** + * Filter skills by its name given an user id. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * skillName String Filter by skill name (through skill id) (optional) + * returns List + **/ +exports.usersUserIdSkillsGET = function(userId,skillName) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = [ "", "" ]; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Retrieve header information for a search operation on users skills in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * skillName String Filter by skill name (through skill id) (optional) + * no response value expected for this operation + **/ +exports.usersUserIdSkillsHEAD = function(userId,skillName) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Create a new User Skill. **Security** - This endpoint is accessible by all authenticated users. + * + * userId String The user id + * body UserSkillRequestBody + * returns UserSkill + **/ +exports.usersUserIdSkillsPOST = function(userId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Remove an existing User Skill with given ids. **Security** - Note that this endpoint is only available for admin users. + * + * userId String The user id + * skillId String The skill id + * no response value expected for this operation + **/ +exports.usersUserIdSkillsSkillIdDELETE = function(userId,skillId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Get User Skills with given user and skill id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * skillId String The skill id + * returns UserSkill + **/ +exports.usersUserIdSkillsSkillIdGET = function(userId,skillId) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + + +/** + * Get User Skills with given ids, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. + * + * userId String The user id + * skillId String The skill id + * no response value expected for this operation + **/ +exports.usersUserIdSkillsSkillIdHEAD = function(userId,skillId) { + return new Promise(function(resolve, reject) { + resolve(); + }); +} + + +/** + * Update an existing skill with given ids. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. + * + * userId String The user id + * skillId String The skill id + * body UserSkillUpdateRequestBody + * returns UserSkill + **/ +exports.usersUserIdSkillsSkillIdPATCH = function(userId,skillId,body) { + return new Promise(function(resolve, reject) { + var examples = {}; + examples['application/json'] = ""; + if (Object.keys(examples).length > 0) { + resolve(examples[Object.keys(examples)[0]]); + } else { + resolve(); + } + }); +} + diff --git a/utils/writer.js b/utils/writer.js new file mode 100644 index 0000000..d79f6e1 --- /dev/null +++ b/utils/writer.js @@ -0,0 +1,43 @@ +var ResponsePayload = function(code, payload) { + this.code = code; + this.payload = payload; +} + +exports.respondWithCode = function(code, payload) { + return new ResponsePayload(code, payload); +} + +var writeJson = exports.writeJson = function(response, arg1, arg2) { + var code; + var payload; + + if(arg1 && arg1 instanceof ResponsePayload) { + writeJson(response, arg1.payload, arg1.code); + return; + } + + if(arg2 && Number.isInteger(arg2)) { + code = arg2; + } + else { + if(arg1 && Number.isInteger(arg1)) { + code = arg1; + } + } + if(code && arg1) { + payload = arg1; + } + else if(arg1) { + payload = arg1; + } + + if(!code) { + // if no response code given, we default to 200 + code = 200; + } + if(typeof payload === 'object') { + payload = JSON.stringify(payload, null, 2); + } + response.writeHead(code, {'Content-Type': 'application/json'}); + response.end(payload); +} From ba99e94d2937ef7c12eb35180134c0c0543c3c5f Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 6 May 2020 16:46:33 +0530 Subject: [PATCH 02/93] Update with the latest swagger specification --- README.md | 13 +- controllers/Users.js | 6 +- docs/UBahn_API_Swagger_Specification.yaml | 3717 --------------------- {api => docs}/swagger.yaml | 28 +- index.js | 4 +- package-lock.json | 444 +-- package.json | 2 +- service/UsersService.js | 6 +- 8 files changed, 198 insertions(+), 4022 deletions(-) delete mode 100644 docs/UBahn_API_Swagger_Specification.yaml rename {api => docs}/swagger.yaml (99%) diff --git a/README.md b/README.md index 3b22396..9233005 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,22 @@ -# u-banh-api +# U-Bahn API + Universal Identity API -## Swagger generated server +## Overview -### Overview This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. -#### Running the server +### Running the server + To run the server, run: -``` +```bash npm start ``` To view the Swagger UI interface: -``` +```bash open http://localhost:8080/docs ``` diff --git a/controllers/Users.js b/controllers/Users.js index 50e9abe..7bb8453 100644 --- a/controllers/Users.js +++ b/controllers/Users.js @@ -5,10 +5,9 @@ var Users = require('../service/UsersService'); module.exports.usersGET = function usersGET (req, res, next) { var handle = req.swagger.params['handle'].value; - var isAvailable = req.swagger.params['isAvailable'].value; var groupId = req.swagger.params['groupId'].value; var roleId = req.swagger.params['roleId'].value; - Users.usersGET(handle,isAvailable,groupId,roleId) + Users.usersGET(handle,groupId,roleId) .then(function (response) { utils.writeJson(res, response); }) @@ -19,10 +18,9 @@ module.exports.usersGET = function usersGET (req, res, next) { module.exports.usersHEAD = function usersHEAD (req, res, next) { var handle = req.swagger.params['handle'].value; - var isAvailable = req.swagger.params['isAvailable'].value; var groupId = req.swagger.params['groupId'].value; var roleId = req.swagger.params['roleId'].value; - Users.usersHEAD(handle,isAvailable,groupId,roleId) + Users.usersHEAD(handle,groupId,roleId) .then(function (response) { utils.writeJson(res, response); }) diff --git a/docs/UBahn_API_Swagger_Specification.yaml b/docs/UBahn_API_Swagger_Specification.yaml deleted file mode 100644 index c6a15b1..0000000 --- a/docs/UBahn_API_Swagger_Specification.yaml +++ /dev/null @@ -1,3717 +0,0 @@ -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' - -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' - -securityDefinitions: - Bearer: - type: apiKey - name: Authorization - in: header - -produces: - - application/json -consumes: - - 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.' - security: - - Bearer: [] - parameters: - - name: handle - type: string - description: Filter by user handle - in: query - required: false - - name: isAvailable - type: boolean - description: Filter by user availability - in: query - required: false - - name: groupId - type: string - format: UUID - description: Filter by user group Id - in: query - required: false - - name: roleId - type: string - format: UUID - description: Filter by user roleId - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on users in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: handle - type: string - description: Filter by user handle - in: query - required: false - - name: isAvailable - type: boolean - description: Filter by user availability - in: query - required: false - - name: groupId - type: string - format: UUID - description: Filter by user group Id - in: query - required: false - - name: roleId - type: string - format: UUID - description: Filter by user roleId - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new User. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /users/{userId}: - get: - description: | - Get User with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - head: - description: | - Get User with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing User with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing User with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - Users - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - - /users/{userId}/skills: - get: - description: | - Filter skills by its name given an user id. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillName - type: string - description: Filter by skill name (through skill id) - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on users skills in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillName - type: string - description: Filter by skill name (through skill id) - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new User Skill. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - - /users/{userId}/skills/{skillId}: - get: - description: | - Get User Skills with given user and skill id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - 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' - head: - description: | - Get User Skills with given ids, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing skill with given ids. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing User Skill with given ids. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'Users Skills' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - 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' - - /skills: - get: - description: | - Get list of skills in the application. If no results, then empty array is returned. Multiple filters are - supported. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills' - security: - - Bearer: [] - 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' - head: - description: | - Retrieve header information for get operation on Skills in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills' - security: - - Bearer: [] - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Skill. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Skills' - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /skills/{skillId}: - get: - description: | - Get Skill by given skill id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills' - security: - - Bearer: [] - parameters: - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - 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' - head: - description: | - Retrieve header information for get operation on Skill by its Id in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills' - security: - - Bearer: [] - parameters: - - name: skillId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing skill with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - Skills - security: - - Bearer: [] - parameters: - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing skill with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - Skills - security: - - Bearer: [] - parameters: - - name: skillId - type: string - description: The skill id - format: UUID - in: path - required: true - 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' - - /skillsProviders: - get: - description: | - Search Skills Provider in the application. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by provider name - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on skills providers in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by provider name - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Skills Provider. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /skillsProviders/{providerId}: - get: - description: | - Get skills provider with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - head: - description: | - Get skills provider with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing skills provider with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing skills provider with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'Skills Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - - /roles: - get: - description: | - Search Roles in the application. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by role name - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on Roles in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by role name - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Role. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /roles/{roleId}: - get: - description: | - Get role with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - 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' - head: - description: | - Get role with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing role with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing role with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - Roles - security: - - Bearer: [] - parameters: - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - 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' - - /users/{userId}/roles: - get: - description: | - Get User Roles that belong to given user id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - head: - description: | - Get User Roles that belong to given user id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - post: - description: | - Create a new User Role. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - - /users/{userId}/roles/{roleId}: - get: - description: | - Get role by its id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - 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' - head: - description: | - Retrieve header information for a search operation on User Roles in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - delete: - description: | - Remove an existing user role with given user and role id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'User Roles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: roleId - type: string - description: The role id - format: UUID - in: path - required: true - 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' - - /users/{userId}/externalProfiles: - get: - description: | - Get External Profiles with given user id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: organizationName - type: string - description: The organization name - in: query - required: false - 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' - head: - description: | - Get External Profiles with given user id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - 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' - post: - description: | - Create a new External Profile for given user id. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - - /users/{userId}/externalProfiles/{organizationId}: - get: - description: | - Get external profile with given user id and organization id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - head: - description: | - Get external profile with given user id and organization id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing external profile with given user id and organization id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing external profile with given user id and organization id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'External Profiles' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - - /users/{userId}/achievements: - get: - description: | - Get Achievements for given user id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsproviderName - type: string - description: The achievement provider name - in: query - required: false - 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' - head: - description: | - Get Achievements for given user id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsproviderName - type: string - description: The achievement provider name - in: query - required: false - 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' - post: - description: | - Create a new Achievement. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - - /users/{userId}/achievements/{achievementsProviderId}: - get: - description: | - Get Achievements for given user id and provider id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsProviderId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - head: - description: | - Get Achievements for given user id and provider id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsProviderId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing Achievement with given userId and achievement providerId. Only the fields in the request body are updated. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsProviderId - type: string - description: The provider id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing Achievement with given userId and achievement providerId. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'Achievements' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: achievementsProviderId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - - /achievementsProviders: - get: - description: | - Search Achievements Provider in the application. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by provider name - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on achivements provider in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by provider name - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Achievements Provider. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /achievementsProviders/{providerId}: - get: - description: | - Get achievements provider with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - head: - description: | - Get achivements provider with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing achivements provider with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing achiements provider with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'Achievements Provider' - security: - - Bearer: [] - parameters: - - name: providerId - type: string - description: The provider id - format: UUID - in: path - required: true - 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' - - /organizations: - get: - description: | - Search organizations in the application. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by organization name - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on organizations in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by organization name - in: query - required: false - responses: - 200: - description: success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Organization. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /organizations/{organizationId}: - get: - description: | - Get organization with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - head: - description: | - Get organization with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing organization with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing organization with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - Organizations - security: - - Bearer: [] - parameters: - - name: organizationId - type: string - description: The organization id - format: UUID - in: path - required: true - 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' - - /users/{userId}/attributes: - get: - description: | - Get attributes for the given user. - Optionally, 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. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeName - type: string - description: Filter by the attribute name - in: query - required: false - - name: attributeGroupName - type: string - description: Filter by the attribute group name - in: query - required: false - - name: attributeGroupId - type: string - description: Filter by the attribute group id - in: query - format: UUID - required: false - 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' - head: - description: | - Retrieve header information for a search operation on users attributes in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeName - type: string - description: Filter by the attribute name - in: query - required: false - - name: attributeGroupName - type: string - description: Filter by the attribute group name - in: query - required: false - - name: attributeGroupId - type: string - description: Filter by the attribute group id - in: query - format: UUID - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new User Attribute. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: body - in: 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' - - /users/{userId}/attributes/{attributeId}: - get: - description: | - Get User Attributes with given user and attribute id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - head: - description: | - Get User Attributes with given ids, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing user attribute with given ids. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing User Attribute with given ids. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'User Attributes' - security: - - Bearer: [] - parameters: - - name: userId - type: string - description: The user id - format: UUID - in: path - required: true - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - - /attributes: - get: - description: | - Get list of attributes in the application. If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attributes' - security: - - Bearer: [] - parameters: - - name: attributeGroupId - type: string - format: UUID - description: Filter by attribute group id - required: false - in: query - - name: name - type: string - description: Filter by attribute name - in: query - required: false - 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' - head: - description: | - Retrieve header information for get operation on Attributes in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attributes' - security: - - Bearer: [] - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Attribute. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Attributes' - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /attributes/{attributeId}: - get: - description: | - Get Attribute by given attribute id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attributes' - security: - - Bearer: [] - parameters: - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - head: - description: | - Retrieve header information for get operation on Attribute by its id in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - Attributes - security: - - Bearer: [] - parameters: - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing attribute with given id. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - Attributes - security: - - Bearer: [] - parameters: - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing attribute with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - Attributes - security: - - Bearer: [] - parameters: - - name: attributeId - type: string - description: The attribute id - format: UUID - in: path - required: true - 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' - - /attributeGroups: - get: - description: | - Search Attribute Groups in the application. Multiple filters are supported. - If no results, then empty array is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by group name - in: query - required: false - - name: organizationId - type: string - format: UUID - description: Filter by organization id - in: query - required: false - 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' - head: - description: | - Retrieve header information for a search operation on Attribute Groups in the application. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: name - type: string - description: Filter by group name - in: query - required: false - - name: organizationId - type: string - format: UUID - description: Filter by organization id - in: query - required: false - responses: - 200: - description: Success response - 400: - $ref: '#/definitions/BadRequest' - 401: - $ref: '#/definitions/Unauthorized' - 403: - $ref: '#/definitions/Forbidden' - 500: - $ref: '#/definitions/ServerError' - post: - description: | - Create a new Attribute Group. - - **Security** - This endpoint is accessible by all authenticated users. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: body - in: 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' - - /attributeGroups/{id}: - get: - description: | - Get Attribute Groups with given id. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: id - type: string - description: The id - format: UUID - in: path - required: true - 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' - head: - description: | - Get Attribute Group with given id, but only header information is returned. - - **Security** - Note that for non-admin users, this endpoint will only return entities that - the user has created. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: id - type: string - description: The id - format: UUID - in: path - required: true - 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' - patch: - description: | - Update an existing Attribute Group with given id. Only the fields in the request body are updated. - - **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the - calling user has created. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: id - type: string - description: The id - format: UUID - in: path - required: true - - name: body - in: 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' - delete: - description: | - Remove an existing Attribute Group with given id. - - **Security** - Note that this endpoint is only available for admin users. - tags: - - 'Attribute Groups' - security: - - Bearer: [] - parameters: - - name: id - type: string - description: The id - format: UUID - in: path - required: true - 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' - -definitions: - AuditFields: - type: object - description: Describes the audit fields that are present in all the models in this API. - required: - - created - - updated - - createdBy - - 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. - - Achievement: - allOf: - - type: object - required: - - userId - - achievementsProviderId - - name - - uri - - certifierId - - certifierDate - 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: - required: - - achievementsProviderId - - name - - uri - - certifierId - - certifierDate - 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. - - 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: - - id - - skillProviderId - - name - - externalId - - 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: - required: - - skillProviderId - - name - - externalId - - uri - 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 - - 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: - - userId - - organizationId - - uri - 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. - - ExternalProfileUpdateRequestBody: - type: object - properties: - uri: - type: string - description: The uri of the external profile. - - 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 - - roleId - properties: - userId: - type: string - format: UUID - description: The user to be associated with the role - - $ref: '#/definitions/UserRoleRequestBody' - - $ref: '#/definitions/AuditFields' - - UserRoleRequestBody: - type: object - description: | - Represents a Role that belongs to a given user. - required: - - roleId - properties: - roleId: - type: string - format: UUID - description: The roleId of this user role. - - User: - allOf: - - type: object - required: - - id - - handle - - isAvailable - properties: - id: - type: string - format: UUID - description: The id of the user. - handle: - type: string - description: The handle of the user. - isAvailable: - type: boolean - description: Indicates the availability of the user. - - $ref: '#/definitions/AuditFields' - - UserSkill: - allOf: - - type: object - required: - - userId - - skillId - - metricValue - - certifierId - - certifiedDate - 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: - required: - - skillId - - metricValue - - certifierId - - certifiedDate - allOf: - - type: object - properties: - skillId: - type: string - format: UUID - description: The Skill id. - - $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. - - UserAttribute: - allOf: - - type: object - required: - - userId - - attributeId - - 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: - required: - - attributeId - - value - 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. - - UserRequestBody: - type: object - description: | - Properties that are provided when creating or editing a User. - properties: - handle: - type: string - description: The handle of the user. - isAvailable: - type: boolean - description: Indicates the availability of the user. - - UserUpdateRequestBody: - type: object - properties: - handle: - type: string - description: The handle of the user. - isAvailable: - type: boolean - description: Indicates the availability of the user. - - 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: - - organizationId - - name - description: Request body containing the fields for an Attribute Group. - 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. - - 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 - description: Simple request body containing the name of the entity. - required: - - name - properties: - name: - type: string - description: Name of the entity - - Attribute: - allOf: - - type: object - required: - - id - - attributeGroupId - - 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: - required: - - attributeGroupId - - name - 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 - - # Schema for error body - Unauthorized: - type: object - description: The unauthorized error entity. - properties: - message: - type: string - description: The unauthorized error message. - example: Unable to authenticate the user. - - NotFound: - type: object - description: The not found error entity. - properties: - message: - type: string - description: The not found error message. - example: A resource with the name could not be found. - - ServerError: - type: object - description: The server error entity. - properties: - message: - type: string - description: The server error message. - 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. - - BadRequest: - type: object - description: The bad request error entity. - properties: - message: - type: string - description: The bad request error message. - example: Invalid input. - - Forbidden: - type: object - description: The permission error entity. - properties: - message: - type: string - description: The forbidden error message. - example: You are not allowed to access the request. - - Conflict: - type: object - description: The conflict error entity. - required: - - message - properties: - message: - type: string - description: The conflict error message. - example: Creating a resource with a name already exists. diff --git a/api/swagger.yaml b/docs/swagger.yaml similarity index 99% rename from api/swagger.yaml rename to docs/swagger.yaml index e437bae..b8fe33d 100644 --- a/api/swagger.yaml +++ b/docs/swagger.yaml @@ -6,6 +6,8 @@ info: \ and expertise for suitability in other projects" version: "1.0.0" title: "UBahn API" +host: "api.ubahn-dev.com" +basePath: "/v5" tags: - name: "Users" description: "Users registered in the system" @@ -53,11 +55,6 @@ paths: description: "Filter by user handle" required: false type: "string" - - name: "isAvailable" - in: "query" - description: "Filter by user availability" - required: false - type: "boolean" - name: "groupId" in: "query" description: "Filter by user group Id" @@ -101,11 +98,6 @@ paths: description: "Filter by user handle" required: false type: "string" - - name: "isAvailable" - in: "query" - description: "Filter by user availability" - required: false - type: "boolean" - name: "groupId" in: "query" description: "Filter by user group Id" @@ -3398,7 +3390,6 @@ definitions: required: - "handle" - "id" - - "isAvailable" properties: id: type: "string" @@ -3407,9 +3398,6 @@ definitions: handle: type: "string" description: "The handle of the user." - isAvailable: - type: "boolean" - description: "Indicates the availability of the user." - $ref: "#/definitions/AuditFields" UserSkill: allOf: @@ -3509,12 +3497,8 @@ definitions: handle: type: "string" description: "The handle of the user." - isAvailable: - type: "boolean" - description: "Indicates the availability of the user." description: "Properties that are provided when creating or editing a User.\n" example: - isAvailable: true handle: "handle" UserUpdateRequestBody: type: "object" @@ -3522,11 +3506,7 @@ definitions: handle: type: "string" description: "The handle of the user." - isAvailable: - type: "boolean" - description: "Indicates the availability of the user." example: - isAvailable: true handle: "handle" AttributeGroup: allOf: @@ -3649,8 +3629,8 @@ definitions: 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\ + 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." diff --git a/index.js b/index.js index 58fb30b..c186d1a 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ var fs = require('fs'), var app = require('connect')(); var swaggerTools = require('swagger-tools'); var jsyaml = require('js-yaml'); -var serverPort = process.env.PORT; +var serverPort = process.env.PORT || 8080; // swaggerRouter configuration var options = { @@ -17,7 +17,7 @@ var options = { }; // The Swagger document (require it, build it programmatically, fetch it from a URL, ...) -var spec = fs.readFileSync(path.join(__dirname,'api/swagger.yaml'), 'utf8'); +var spec = fs.readFileSync(path.join(__dirname,'docs/swagger.yaml'), 'utf8'); var swaggerDoc = jsyaml.safeLoad(spec); // Initialize the Swagger middleware diff --git a/package-lock.json b/package-lock.json index 7d2c72d..e937e75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,12 @@ } }, "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + "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", @@ -28,51 +31,26 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "body-parser": { - "version": "1.12.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.12.4.tgz", - "integrity": "sha1-CQcAxLoohiqFIO83g5X97l9hwik=", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", "requires": { - "bytes": "1.0.0", - "content-type": "~1.0.1", - "debug": "~2.2.0", - "depd": "~1.0.1", - "iconv-lite": "0.4.8", - "on-finished": "~2.2.1", - "qs": "2.4.2", - "raw-body": "~2.0.1", - "type-is": "~1.6.2" + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" }, "dependencies": { - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "requires": { - "ms": "0.7.1" - } - }, - "ee-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz", - "integrity": "sha1-ag18YiHkkP7v2S7D9EHJzozQl/Q=" - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" - }, - "on-finished": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.2.1.tgz", - "integrity": "sha1-XIXBzDYpn3gCllP2Z/J7a5nrwCk=", - "requires": { - "ee-first": "1.1.0" - } - }, "qs": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.4.2.tgz", - "integrity": "sha1-9854jld33wtQENp/fE5zujJHD1o=" + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" } } }, @@ -114,9 +92,9 @@ } }, "bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha1-NWnt6Lo0MV+rmcPpLLBMciDeH6g=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "combined-stream": { "version": "1.0.8", @@ -127,9 +105,9 @@ } }, "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" }, "component-emitter": { "version": "1.3.0", @@ -192,9 +170,9 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "depd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz", - "integrity": "sha1-gK7GTJ1tl+ZcwqnKqTwKpqv3Oqo=" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" }, "destroy": { "version": "1.0.4", @@ -306,33 +284,25 @@ } }, "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "requires": { "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - } + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" } }, "iconv-lite": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.8.tgz", - "integrity": "sha1-xgGadZXyzvynAuq2lKAQvNkpjSA=" + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "isarray": { "version": "1.0.0", @@ -349,17 +319,25 @@ } }, "json-refs": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz", - "integrity": "sha1-uesB/in16j6Sh48VrqEK04taz4k=", - "requires": { - "commander": "^2.9.0", - "graphlib": "^2.1.1", - "js-yaml": "^3.8.3", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-3.0.15.tgz", + "integrity": "sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==", + "requires": { + "commander": "~4.1.1", + "graphlib": "^2.1.8", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", "native-promise-only": "^0.8.1", - "path-loader": "^1.0.2", - "slash": "^1.0.0", - "uri-js": "^3.0.2" + "path-loader": "^1.0.10", + "slash": "^3.0.0", + "uri-js": "^4.2.2" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" + } } }, "lodash": { @@ -367,11 +345,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, - "lodash-compat": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/lodash-compat/-/lodash-compat-3.10.2.tgz", - "integrity": "sha1-xpQBKKnTD46QLNLPmf0Muk7PwYM=" - }, "lodash._arraypool": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz", @@ -697,59 +670,12 @@ "requires": { "native-promise-only": "^0.8.1", "superagent": "^3.8.3" - }, - "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==" - }, - "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" - }, - "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" - } - } } }, "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - } - } + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" }, "process-nextick-args": { "version": "2.0.1", @@ -762,9 +688,9 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz", - "integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc=" + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "range-parser": { "version": "1.2.1", @@ -772,18 +698,36 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.0.2.tgz", - "integrity": "sha1-osL5jIUxzumcY9jSOLfel7tln8o=", - "requires": { - "bytes": "2.1.0", - "iconv-lite": "0.4.8" + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" }, "dependencies": { - "bytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.1.0.tgz", - "integrity": "sha1-rJPEEOL/ycx89LRks4KJBn9eR7Q=" + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" } } }, @@ -801,11 +745,6 @@ "util-deprecate": "~1.0.1" } }, - "reduce-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/reduce-component/-/reduce-component-1.0.1.tgz", - "integrity": "sha1-4Mk1QsV0UhvqE98PlIjtgqt3xdo=" - }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -831,15 +770,32 @@ "statuses": "~1.5.0" }, "dependencies": { - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "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==" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" } } }, @@ -855,19 +811,19 @@ } }, "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "spark-md5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-1.0.1.tgz", - "integrity": "sha1-xLmo1Bz3sIRUI6ghgk+N/6D1G3w=" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz", + "integrity": "sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig==" }, "sprintf-js": { "version": "1.0.3", @@ -884,11 +840,6 @@ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" }, - "string": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/string/-/string-3.3.3.tgz", - "integrity": "sha1-XqIRzZLSKOGEKUmQpsyXs2anfLA=" - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -898,83 +849,34 @@ } }, "superagent": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-1.8.5.tgz", - "integrity": "sha1-HA3cOvMOgOuE68BcshItqP6UC1U=", - "requires": { - "component-emitter": "~1.2.0", - "cookiejar": "2.0.6", - "debug": "2", - "extend": "3.0.0", - "form-data": "1.0.0-rc3", - "formidable": "~1.0.14", - "methods": "~1.1.1", - "mime": "1.3.4", - "qs": "2.3.3", - "readable-stream": "1.0.27-1", - "reduce-component": "1.0.1" + "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": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, - "cookiejar": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.0.6.tgz", - "integrity": "sha1-Cr81atANHFohnYjURRgEbdAmrP4=" - }, - "extend": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz", - "integrity": "sha1-WkdDU7nzNT3dgXbf03uRyDpG8dQ=" - }, - "form-data": { - "version": "1.0.0-rc3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.0-rc3.tgz", - "integrity": "sha1-01vGLn+8KTeuePlIqqDTjZBgdXc=", - "requires": { - "async": "^1.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.3" - } - }, - "formidable": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.16.tgz", - "integrity": "sha1-SRbP38TL7QILJXpqlQWpqzjCzQ4=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "mime": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", - "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" - }, - "qs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", - "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=" - }, - "readable-stream": { - "version": "1.0.27-1", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", - "integrity": "sha1-a2eYPCA1fO/QfwFlABoW1xDZEHg=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "ms": "^2.1.1" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -987,28 +889,42 @@ } }, "swagger-tools": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/swagger-tools/-/swagger-tools-0.10.1.tgz", - "integrity": "sha1-pFWyV5mOzyDeW06Zvc8acJqw/tw=", - "requires": { - "async": "^1.3.0", - "body-parser": "1.12.4", - "commander": "^2.8.1", - "debug": "^2.2.0", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/swagger-tools/-/swagger-tools-0.10.4.tgz", + "integrity": "sha512-VQpijIi8cpB/frUZOZlVpS7U3CrdSAZBfiHu448R1njiNXUnE7heF3Svz3qFBr5SYtaPvaqWpHMbvboirCXVzA==", + "requires": { + "async": "^2.5.0", + "body-parser": "1.18.2", + "commander": "~2.11.0", + "debug": "^3.1.0", "js-yaml": "^3.3.1", - "json-refs": "^2.1.5", - "lodash-compat": "^3.10.0", + "json-refs": "^3.0.2", + "lodash": "^4.17.4", "multer": "^1.1.0", "parseurl": "^1.3.0", - "path-to-regexp": "^1.2.0", - "qs": "^4.0.0", + "path-to-regexp": "^2.0.0", + "qs": "^6.0.3", "serve-static": "^1.10.0", - "spark-md5": "^1.0.0", - "string": "^3.3.0", - "superagent": "^1.2.0", + "spark-md5": "^3.0.0", + "superagent": "^3.5.2", "swagger-converter": "^0.1.7", "traverse": "^0.6.6", "z-schema": "^3.15.4" + }, + "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==" + } } }, "toidentifier": { @@ -1041,9 +957,9 @@ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "uri-js": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", - "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "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" } diff --git a/package.json b/package.json index 803729f..3eb36a7 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,6 @@ "dependencies": { "connect": "^3.2.0", "js-yaml": "^3.3.0", - "swagger-tools": "0.10.1" + "swagger-tools": "^0.10.4" } } diff --git a/service/UsersService.js b/service/UsersService.js index c522706..0fc7473 100644 --- a/service/UsersService.js +++ b/service/UsersService.js @@ -5,12 +5,11 @@ * **Point to note** - For non-admin users, this endpoint will only return entities that the user has created. * * handle String Filter by user handle (optional) - * isAvailable Boolean Filter by user availability (optional) * groupId String Filter by user group Id (optional) * roleId String Filter by user roleId (optional) * returns List **/ -exports.usersGET = function(handle,isAvailable,groupId,roleId) { +exports.usersGET = function(handle,groupId,roleId) { return new Promise(function(resolve, reject) { var examples = {}; examples['application/json'] = [ "", "" ]; @@ -27,12 +26,11 @@ exports.usersGET = function(handle,isAvailable,groupId,roleId) { * Retrieve header information for a search operation on users in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. * * handle String Filter by user handle (optional) - * isAvailable Boolean Filter by user availability (optional) * groupId String Filter by user group Id (optional) * roleId String Filter by user roleId (optional) * no response value expected for this operation **/ -exports.usersHEAD = function(handle,isAvailable,groupId,roleId) { +exports.usersHEAD = function(handle,groupId,roleId) { return new Promise(function(resolve, reject) { resolve(); }); From 3486eb25febd4d00c2d94b22c2cd41c5ec7bf7d0 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 13 May 2020 16:53:24 +0530 Subject: [PATCH 03/93] enable cors --- index.js | 5 ++++- package-lock.json | 14 ++++++++++++++ package.json | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index c186d1a..ace12f9 100644 --- a/index.js +++ b/index.js @@ -2,7 +2,8 @@ var fs = require('fs'), path = require('path'), - http = require('http'); + http = require('http'), + cors = require('cors'); var app = require('connect')(); var swaggerTools = require('swagger-tools'); @@ -20,6 +21,8 @@ var options = { var spec = fs.readFileSync(path.join(__dirname,'docs/swagger.yaml'), 'utf8'); var swaggerDoc = jsyaml.safeLoad(spec); +app.use(cors()) + // Initialize the Swagger middleware swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) { diff --git a/package-lock.json b/package-lock.json index e937e75..22ac14e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -156,6 +156,15 @@ "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" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -979,6 +988,11 @@ "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 3eb36a7..a3a643d 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "private": true, "dependencies": { "connect": "^3.2.0", + "cors": "^2.8.5", "js-yaml": "^3.3.0", "swagger-tools": "^0.10.4" } From 2717d64ecb7535cd0bc6c3e2f2b04f5e9f2708a6 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 20 May 2020 12:05:57 +0530 Subject: [PATCH 04/93] Implement API using QLDB --- .dockerignore | 6 + .gitignore | 108 +- .swagger-codegen-ignore | 23 - .swagger-codegen/VERSION | 1 - Dockerfile | 10 + Procfile | 1 - README.md | 165 +- app.js | 115 + config/default.js | 17 + config/production.js | 7 + controllers/Achievements.js | 89 - controllers/AchievementsProvider.js | 82 - controllers/AttributeGroups.js | 84 - controllers/Attributes.js | 82 - controllers/ExternalProfiles.js | 88 - controllers/Organizations.js | 82 - controllers/Roles.js | 82 - controllers/Skills.js | 80 - controllers/SkillsProvider.js | 82 - controllers/UserAttributes.js | 93 - controllers/UserRoles.js | 74 - controllers/Users.js | 86 - controllers/UsersSkills.js | 89 - docs/UBahn_API.postman_collection.json | 3104 +++++++++++++++ docs/UBahn_ENV.postman_environment.json | 59 + docs/swagger.yaml | 4 +- index.js | 47 - package-lock.json | 3536 ++++++++++++++--- package.json | 42 +- scripts/db/data/Achievement.json | 28 + scripts/db/data/AchievementsProvider.json | 26 + scripts/db/data/Attribute.json | 29 + scripts/db/data/AttributeGroup.json | 20 + scripts/db/data/ExternalProfile.json | 12 + scripts/db/data/Organization.json | 10 + scripts/db/data/Role.json | 18 + scripts/db/data/Skill.json | 13 + scripts/db/data/SkillsProvider.json | 18 + scripts/db/data/User.json | 18 + scripts/db/data/UsersAttribute.json | 22 + scripts/db/data/UsersRole.json | 11 + scripts/db/data/UsersSkill.json | 14 + scripts/db/dropAll.js | 24 + scripts/db/genData.js | 33 + service/AchievementsProviderService.js | 118 - service/AchievementsService.js | 125 - service/AttributeGroupsService.js | 120 - service/AttributesService.js | 118 - service/ExternalProfilesService.js | 124 - service/OrganizationsService.js | 118 - service/RolesService.js | 118 - service/SkillsProviderService.js | 118 - service/SkillsService.js | 116 - service/UserAttributesService.js | 129 - service/UserRolesService.js | 102 - service/UsersService.js | 122 - service/UsersSkillsService.js | 125 - src/bootstrap.js | 29 + src/common/controller-helper.js | 134 + src/common/error.middleware.js | 30 + src/common/errors.js | 25 + src/common/helper.js | 186 + src/common/logger.js | 152 + src/common/service-helper.js | 204 + src/consts.js | 26 + src/models/Achievement.js | 19 + src/models/AchievementsProvider.js | 17 + src/models/Attribute.js | 18 + src/models/AttributeGroup.js | 18 + src/models/BaseObject.js | 48 + src/models/DBHelper.js | 191 + src/models/ExternalProfile.js | 17 + src/models/Organization.js | 17 + src/models/Role.js | 17 + src/models/Skill.js | 17 + src/models/SkillsProvider.js | 14 + src/models/User.js | 21 + src/models/UserAttribute.js | 16 + src/models/UsersRole.js | 16 + src/models/UsersSkill.js | 18 + src/models/index.js | 58 + src/modules/achievement/controller.js | 11 + src/modules/achievement/route.js | 54 + src/modules/achievement/service.js | 45 + .../achievementsProvider/controller.js | 11 + src/modules/achievementsProvider/route.js | 54 + src/modules/achievementsProvider/service.js | 32 + src/modules/attribute/controller.js | 11 + src/modules/attribute/route.js | 54 + src/modules/attribute/service.js | 39 + src/modules/attributeGroup/controller.js | 11 + src/modules/attributeGroup/route.js | 54 + src/modules/attributeGroup/service.js | 38 + src/modules/externalProfile/controller.js | 11 + src/modules/externalProfile/route.js | 54 + src/modules/externalProfile/service.js | 39 + src/modules/organization/controller.js | 11 + src/modules/organization/route.js | 54 + src/modules/organization/service.js | 23 + src/modules/role/controller.js | 11 + src/modules/role/route.js | 54 + src/modules/role/service.js | 23 + src/modules/skill/controller.js | 11 + src/modules/skill/route.js | 54 + src/modules/skill/service.js | 27 + src/modules/skillsProvider/controller.js | 11 + src/modules/skillsProvider/route.js | 54 + src/modules/skillsProvider/service.js | 23 + src/modules/user/controller.js | 11 + src/modules/user/route.js | 55 + src/modules/user/service.js | 30 + src/modules/usersAttribute/controller.js | 11 + src/modules/usersAttribute/route.js | 54 + src/modules/usersAttribute/service.js | 50 + src/modules/usersRole/controller.js | 11 + src/modules/usersRole/route.js | 54 + src/modules/usersRole/service.js | 26 + src/modules/usersSkill/controller.js | 11 + src/modules/usersSkill/route.js | 54 + src/modules/usersSkill/service.js | 38 + src/route.js | 29 + utils/writer.js | 43 - 122 files changed, 9389 insertions(+), 3457 deletions(-) create mode 100644 .dockerignore delete mode 100644 .swagger-codegen-ignore delete mode 100644 .swagger-codegen/VERSION create mode 100644 Dockerfile delete mode 100644 Procfile mode change 100644 => 100755 README.md create mode 100755 app.js create mode 100755 config/default.js create mode 100755 config/production.js delete mode 100644 controllers/Achievements.js delete mode 100644 controllers/AchievementsProvider.js delete mode 100644 controllers/AttributeGroups.js delete mode 100644 controllers/Attributes.js delete mode 100644 controllers/ExternalProfiles.js delete mode 100644 controllers/Organizations.js delete mode 100644 controllers/Roles.js delete mode 100644 controllers/Skills.js delete mode 100644 controllers/SkillsProvider.js delete mode 100644 controllers/UserAttributes.js delete mode 100644 controllers/UserRoles.js delete mode 100644 controllers/Users.js delete mode 100644 controllers/UsersSkills.js create mode 100644 docs/UBahn_API.postman_collection.json create mode 100644 docs/UBahn_ENV.postman_environment.json delete mode 100644 index.js mode change 100644 => 100755 package.json create mode 100644 scripts/db/data/Achievement.json create mode 100644 scripts/db/data/AchievementsProvider.json create mode 100644 scripts/db/data/Attribute.json create mode 100644 scripts/db/data/AttributeGroup.json create mode 100644 scripts/db/data/ExternalProfile.json create mode 100644 scripts/db/data/Organization.json create mode 100644 scripts/db/data/Role.json create mode 100644 scripts/db/data/Skill.json create mode 100644 scripts/db/data/SkillsProvider.json create mode 100644 scripts/db/data/User.json create mode 100644 scripts/db/data/UsersAttribute.json create mode 100644 scripts/db/data/UsersRole.json create mode 100644 scripts/db/data/UsersSkill.json create mode 100644 scripts/db/dropAll.js create mode 100644 scripts/db/genData.js delete mode 100644 service/AchievementsProviderService.js delete mode 100644 service/AchievementsService.js delete mode 100644 service/AttributeGroupsService.js delete mode 100644 service/AttributesService.js delete mode 100644 service/ExternalProfilesService.js delete mode 100644 service/OrganizationsService.js delete mode 100644 service/RolesService.js delete mode 100644 service/SkillsProviderService.js delete mode 100644 service/SkillsService.js delete mode 100644 service/UserAttributesService.js delete mode 100644 service/UserRolesService.js delete mode 100644 service/UsersService.js delete mode 100644 service/UsersSkillsService.js create mode 100755 src/bootstrap.js create mode 100644 src/common/controller-helper.js create mode 100755 src/common/error.middleware.js create mode 100644 src/common/errors.js create mode 100644 src/common/helper.js create mode 100755 src/common/logger.js create mode 100644 src/common/service-helper.js create mode 100644 src/consts.js create mode 100644 src/models/Achievement.js create mode 100644 src/models/AchievementsProvider.js create mode 100644 src/models/Attribute.js create mode 100644 src/models/AttributeGroup.js create mode 100644 src/models/BaseObject.js create mode 100644 src/models/DBHelper.js create mode 100644 src/models/ExternalProfile.js create mode 100644 src/models/Organization.js create mode 100644 src/models/Role.js create mode 100644 src/models/Skill.js create mode 100644 src/models/SkillsProvider.js create mode 100644 src/models/User.js create mode 100644 src/models/UserAttribute.js create mode 100644 src/models/UsersRole.js create mode 100644 src/models/UsersSkill.js create mode 100755 src/models/index.js create mode 100644 src/modules/achievement/controller.js create mode 100644 src/modules/achievement/route.js create mode 100644 src/modules/achievement/service.js create mode 100644 src/modules/achievementsProvider/controller.js create mode 100644 src/modules/achievementsProvider/route.js create mode 100644 src/modules/achievementsProvider/service.js create mode 100644 src/modules/attribute/controller.js create mode 100644 src/modules/attribute/route.js create mode 100644 src/modules/attribute/service.js create mode 100644 src/modules/attributeGroup/controller.js create mode 100644 src/modules/attributeGroup/route.js create mode 100644 src/modules/attributeGroup/service.js create mode 100644 src/modules/externalProfile/controller.js create mode 100644 src/modules/externalProfile/route.js create mode 100644 src/modules/externalProfile/service.js create mode 100644 src/modules/organization/controller.js create mode 100644 src/modules/organization/route.js create mode 100644 src/modules/organization/service.js create mode 100644 src/modules/role/controller.js create mode 100644 src/modules/role/route.js create mode 100644 src/modules/role/service.js create mode 100644 src/modules/skill/controller.js create mode 100644 src/modules/skill/route.js create mode 100644 src/modules/skill/service.js create mode 100644 src/modules/skillsProvider/controller.js create mode 100644 src/modules/skillsProvider/route.js create mode 100644 src/modules/skillsProvider/service.js create mode 100644 src/modules/user/controller.js create mode 100644 src/modules/user/route.js create mode 100644 src/modules/user/service.js create mode 100644 src/modules/usersAttribute/controller.js create mode 100644 src/modules/usersAttribute/route.js create mode 100644 src/modules/usersAttribute/service.js create mode 100644 src/modules/usersRole/controller.js create mode 100644 src/modules/usersRole/route.js create mode 100644 src/modules/usersRole/service.js create mode 100644 src/modules/usersSkill/controller.js create mode 100644 src/modules/usersSkill/route.js create mode 100644 src/modules/usersSkill/service.js create mode 100755 src/route.js delete mode 100644 utils/writer.js diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..28f8ecb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +.idea +**/.DS_Store +docs +.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/.swagger-codegen-ignore b/.swagger-codegen-ignore deleted file mode 100644 index c5fa491..0000000 --- a/.swagger-codegen-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# Swagger Codegen Ignore -# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/.swagger-codegen/VERSION b/.swagger-codegen/VERSION deleted file mode 100644 index 9c84656..0000000 --- a/.swagger-codegen/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.4.13 \ No newline at end of file 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/Procfile b/Procfile deleted file mode 100644 index 5ec9cc2..0000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node index.js \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 9233005..32fef12 --- a/README.md +++ b/README.md @@ -1,23 +1,162 @@ # U-Bahn API -Universal Identity API +## Install software -## Overview +- node 12.x +- npm 6.x +- docker -This server was generated by the [swagger-codegen](https://github.com/swagger-api/swagger-codegen) project. By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +## Local deployment -### Running the server +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` -To run the server, run: +## Docker -```bash -npm start -``` +Make sure all config values are right(aws key and secret), and you can run on local successful, then run below commands -To view the Swagger UI interface: +- Run `docker build -t tc/ubahn_api .` to build image +- Then run `docker run tc/ubahn_api -d` to startup image -```bash -open http://localhost:8080/docs -``` +## API endpoints verification -This project leverages the mega-awesome [swagger-tools](https://github.com/apigee-127/swagger-tools) middleware which does most all the work. +1. open postman +2. import *docs/UBahn_API.postman_collection.json* , *UBahn_ENV.postman_environment.json* and then check endpoints + +## Configuration + +| key | system Environment name | description | +| ------------- | ----------------------- | -------------------------- | +| PORT | PORT | the server port | +| AUTH_SECRET | AUTH_SECRET | the jwt client secret | +| VALID_ISSUERS | VALID_ISSUERS | jwt token issuers | +| API_VERSION | | the api prefix version | +| AWS_KEY | AWS_KEY | the aws Access key | +| AWS_SECRET | AWS_SECRET | the aws Access secret | +| AWS_REGION | AWS_REGION | the aws service region | +| DATABASE | DATABASE | the aws QLDB database name | + +## 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 + + ```json + 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/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/config/default.js b/config/default.js new file mode 100755 index 0000000..356a4f5 --- /dev/null +++ b/config/default.js @@ -0,0 +1,17 @@ +/** + * 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"]', + API_VERSION: 'api/1.0', + + AWS_KEY: process.env.AWS_KEY, + AWS_SECRET: process.env.AWS_SECRET, + AWS_REGION: process.env.AWS_REGION || 'us-east-1', + DATABASE: process.env.DATABASE || 'ubahn-db' +} 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/controllers/Achievements.js b/controllers/Achievements.js deleted file mode 100644 index cccd0cb..0000000 --- a/controllers/Achievements.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Achievements = require('../service/AchievementsService'); - -module.exports.usersUserIdAchievementsAchievementsProviderIdDELETE = function usersUserIdAchievementsAchievementsProviderIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; - Achievements.usersUserIdAchievementsAchievementsProviderIdDELETE(userId,achievementsProviderId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsAchievementsProviderIdGET = function usersUserIdAchievementsAchievementsProviderIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; - Achievements.usersUserIdAchievementsAchievementsProviderIdGET(userId,achievementsProviderId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsAchievementsProviderIdHEAD = function usersUserIdAchievementsAchievementsProviderIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; - Achievements.usersUserIdAchievementsAchievementsProviderIdHEAD(userId,achievementsProviderId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsAchievementsProviderIdPATCH = function usersUserIdAchievementsAchievementsProviderIdPATCH (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsProviderId = req.swagger.params['achievementsProviderId'].value; - var body = req.swagger.params['body'].value; - Achievements.usersUserIdAchievementsAchievementsProviderIdPATCH(userId,achievementsProviderId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsGET = function usersUserIdAchievementsGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsproviderName = req.swagger.params['achievementsproviderName'].value; - Achievements.usersUserIdAchievementsGET(userId,achievementsproviderName) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsHEAD = function usersUserIdAchievementsHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var achievementsproviderName = req.swagger.params['achievementsproviderName'].value; - Achievements.usersUserIdAchievementsHEAD(userId,achievementsproviderName) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAchievementsPOST = function usersUserIdAchievementsPOST (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - Achievements.usersUserIdAchievementsPOST(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/AchievementsProvider.js b/controllers/AchievementsProvider.js deleted file mode 100644 index e1248c9..0000000 --- a/controllers/AchievementsProvider.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var AchievementsProvider = require('../service/AchievementsProviderService'); - -module.exports.achievementsProvidersGET = function achievementsProvidersGET (req, res, next) { - var name = req.swagger.params['name'].value; - AchievementsProvider.achievementsProvidersGET(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersHEAD = function achievementsProvidersHEAD (req, res, next) { - var name = req.swagger.params['name'].value; - AchievementsProvider.achievementsProvidersHEAD(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersPOST = function achievementsProvidersPOST (req, res, next) { - var body = req.swagger.params['body'].value; - AchievementsProvider.achievementsProvidersPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersProviderIdDELETE = function achievementsProvidersProviderIdDELETE (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - AchievementsProvider.achievementsProvidersProviderIdDELETE(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersProviderIdGET = function achievementsProvidersProviderIdGET (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - AchievementsProvider.achievementsProvidersProviderIdGET(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersProviderIdHEAD = function achievementsProvidersProviderIdHEAD (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - AchievementsProvider.achievementsProvidersProviderIdHEAD(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.achievementsProvidersProviderIdPATCH = function achievementsProvidersProviderIdPATCH (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - var body = req.swagger.params['body'].value; - AchievementsProvider.achievementsProvidersProviderIdPATCH(providerId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/AttributeGroups.js b/controllers/AttributeGroups.js deleted file mode 100644 index 3d2cd95..0000000 --- a/controllers/AttributeGroups.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var AttributeGroups = require('../service/AttributeGroupsService'); - -module.exports.attributeGroupsGET = function attributeGroupsGET (req, res, next) { - var name = req.swagger.params['name'].value; - var organizationId = req.swagger.params['organizationId'].value; - AttributeGroups.attributeGroupsGET(name,organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsHEAD = function attributeGroupsHEAD (req, res, next) { - var name = req.swagger.params['name'].value; - var organizationId = req.swagger.params['organizationId'].value; - AttributeGroups.attributeGroupsHEAD(name,organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsIdDELETE = function attributeGroupsIdDELETE (req, res, next) { - var id = req.swagger.params['id'].value; - AttributeGroups.attributeGroupsIdDELETE(id) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsIdGET = function attributeGroupsIdGET (req, res, next) { - var id = req.swagger.params['id'].value; - AttributeGroups.attributeGroupsIdGET(id) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsIdHEAD = function attributeGroupsIdHEAD (req, res, next) { - var id = req.swagger.params['id'].value; - AttributeGroups.attributeGroupsIdHEAD(id) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsIdPATCH = function attributeGroupsIdPATCH (req, res, next) { - var id = req.swagger.params['id'].value; - var body = req.swagger.params['body'].value; - AttributeGroups.attributeGroupsIdPATCH(id,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributeGroupsPOST = function attributeGroupsPOST (req, res, next) { - var body = req.swagger.params['body'].value; - AttributeGroups.attributeGroupsPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/Attributes.js b/controllers/Attributes.js deleted file mode 100644 index 9bca5cf..0000000 --- a/controllers/Attributes.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Attributes = require('../service/AttributesService'); - -module.exports.attributesAttributeIdDELETE = function attributesAttributeIdDELETE (req, res, next) { - var attributeId = req.swagger.params['attributeId'].value; - Attributes.attributesAttributeIdDELETE(attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesAttributeIdGET = function attributesAttributeIdGET (req, res, next) { - var attributeId = req.swagger.params['attributeId'].value; - Attributes.attributesAttributeIdGET(attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesAttributeIdHEAD = function attributesAttributeIdHEAD (req, res, next) { - var attributeId = req.swagger.params['attributeId'].value; - Attributes.attributesAttributeIdHEAD(attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesAttributeIdPATCH = function attributesAttributeIdPATCH (req, res, next) { - var attributeId = req.swagger.params['attributeId'].value; - var body = req.swagger.params['body'].value; - Attributes.attributesAttributeIdPATCH(attributeId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesGET = function attributesGET (req, res, next) { - var attributeGroupId = req.swagger.params['attributeGroupId'].value; - var name = req.swagger.params['name'].value; - Attributes.attributesGET(attributeGroupId,name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesHEAD = function attributesHEAD (req, res, next) { - Attributes.attributesHEAD() - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.attributesPOST = function attributesPOST (req, res, next) { - var body = req.swagger.params['body'].value; - Attributes.attributesPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/ExternalProfiles.js b/controllers/ExternalProfiles.js deleted file mode 100644 index 0b8c907..0000000 --- a/controllers/ExternalProfiles.js +++ /dev/null @@ -1,88 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var ExternalProfiles = require('../service/ExternalProfilesService'); - -module.exports.usersUserIdExternalProfilesGET = function usersUserIdExternalProfilesGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var organizationName = req.swagger.params['organizationName'].value; - ExternalProfiles.usersUserIdExternalProfilesGET(userId,organizationName) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesHEAD = function usersUserIdExternalProfilesHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - ExternalProfiles.usersUserIdExternalProfilesHEAD(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesOrganizationIdDELETE = function usersUserIdExternalProfilesOrganizationIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - var organizationId = req.swagger.params['organizationId'].value; - ExternalProfiles.usersUserIdExternalProfilesOrganizationIdDELETE(userId,organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesOrganizationIdGET = function usersUserIdExternalProfilesOrganizationIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var organizationId = req.swagger.params['organizationId'].value; - ExternalProfiles.usersUserIdExternalProfilesOrganizationIdGET(userId,organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesOrganizationIdHEAD = function usersUserIdExternalProfilesOrganizationIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var organizationId = req.swagger.params['organizationId'].value; - ExternalProfiles.usersUserIdExternalProfilesOrganizationIdHEAD(userId,organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesOrganizationIdPATCH = function usersUserIdExternalProfilesOrganizationIdPATCH (req, res, next) { - var userId = req.swagger.params['userId'].value; - var organizationId = req.swagger.params['organizationId'].value; - var body = req.swagger.params['body'].value; - ExternalProfiles.usersUserIdExternalProfilesOrganizationIdPATCH(userId,organizationId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdExternalProfilesPOST = function usersUserIdExternalProfilesPOST (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - ExternalProfiles.usersUserIdExternalProfilesPOST(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/Organizations.js b/controllers/Organizations.js deleted file mode 100644 index 2e79628..0000000 --- a/controllers/Organizations.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Organizations = require('../service/OrganizationsService'); - -module.exports.organizationsGET = function organizationsGET (req, res, next) { - var name = req.swagger.params['name'].value; - Organizations.organizationsGET(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsHEAD = function organizationsHEAD (req, res, next) { - var name = req.swagger.params['name'].value; - Organizations.organizationsHEAD(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsOrganizationIdDELETE = function organizationsOrganizationIdDELETE (req, res, next) { - var organizationId = req.swagger.params['organizationId'].value; - Organizations.organizationsOrganizationIdDELETE(organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsOrganizationIdGET = function organizationsOrganizationIdGET (req, res, next) { - var organizationId = req.swagger.params['organizationId'].value; - Organizations.organizationsOrganizationIdGET(organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsOrganizationIdHEAD = function organizationsOrganizationIdHEAD (req, res, next) { - var organizationId = req.swagger.params['organizationId'].value; - Organizations.organizationsOrganizationIdHEAD(organizationId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsOrganizationIdPATCH = function organizationsOrganizationIdPATCH (req, res, next) { - var organizationId = req.swagger.params['organizationId'].value; - var body = req.swagger.params['body'].value; - Organizations.organizationsOrganizationIdPATCH(organizationId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.organizationsPOST = function organizationsPOST (req, res, next) { - var body = req.swagger.params['body'].value; - Organizations.organizationsPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/Roles.js b/controllers/Roles.js deleted file mode 100644 index 83e4447..0000000 --- a/controllers/Roles.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Roles = require('../service/RolesService'); - -module.exports.rolesGET = function rolesGET (req, res, next) { - var name = req.swagger.params['name'].value; - Roles.rolesGET(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesHEAD = function rolesHEAD (req, res, next) { - var name = req.swagger.params['name'].value; - Roles.rolesHEAD(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesPOST = function rolesPOST (req, res, next) { - var body = req.swagger.params['body'].value; - Roles.rolesPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesRoleIdDELETE = function rolesRoleIdDELETE (req, res, next) { - var roleId = req.swagger.params['roleId'].value; - Roles.rolesRoleIdDELETE(roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesRoleIdGET = function rolesRoleIdGET (req, res, next) { - var roleId = req.swagger.params['roleId'].value; - Roles.rolesRoleIdGET(roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesRoleIdHEAD = function rolesRoleIdHEAD (req, res, next) { - var roleId = req.swagger.params['roleId'].value; - Roles.rolesRoleIdHEAD(roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.rolesRoleIdPATCH = function rolesRoleIdPATCH (req, res, next) { - var roleId = req.swagger.params['roleId'].value; - var body = req.swagger.params['body'].value; - Roles.rolesRoleIdPATCH(roleId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/Skills.js b/controllers/Skills.js deleted file mode 100644 index 76d2680..0000000 --- a/controllers/Skills.js +++ /dev/null @@ -1,80 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Skills = require('../service/SkillsService'); - -module.exports.skillsGET = function skillsGET (req, res, next) { - Skills.skillsGET() - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsHEAD = function skillsHEAD (req, res, next) { - Skills.skillsHEAD() - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsPOST = function skillsPOST (req, res, next) { - var body = req.swagger.params['body'].value; - Skills.skillsPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsSkillIdDELETE = function skillsSkillIdDELETE (req, res, next) { - var skillId = req.swagger.params['skillId'].value; - Skills.skillsSkillIdDELETE(skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsSkillIdGET = function skillsSkillIdGET (req, res, next) { - var skillId = req.swagger.params['skillId'].value; - Skills.skillsSkillIdGET(skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsSkillIdHEAD = function skillsSkillIdHEAD (req, res, next) { - var skillId = req.swagger.params['skillId'].value; - Skills.skillsSkillIdHEAD(skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsSkillIdPATCH = function skillsSkillIdPATCH (req, res, next) { - var skillId = req.swagger.params['skillId'].value; - var body = req.swagger.params['body'].value; - Skills.skillsSkillIdPATCH(skillId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/SkillsProvider.js b/controllers/SkillsProvider.js deleted file mode 100644 index 642a12d..0000000 --- a/controllers/SkillsProvider.js +++ /dev/null @@ -1,82 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var SkillsProvider = require('../service/SkillsProviderService'); - -module.exports.skillsProvidersGET = function skillsProvidersGET (req, res, next) { - var name = req.swagger.params['name'].value; - SkillsProvider.skillsProvidersGET(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersHEAD = function skillsProvidersHEAD (req, res, next) { - var name = req.swagger.params['name'].value; - SkillsProvider.skillsProvidersHEAD(name) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersPOST = function skillsProvidersPOST (req, res, next) { - var body = req.swagger.params['body'].value; - SkillsProvider.skillsProvidersPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersProviderIdDELETE = function skillsProvidersProviderIdDELETE (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - SkillsProvider.skillsProvidersProviderIdDELETE(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersProviderIdGET = function skillsProvidersProviderIdGET (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - SkillsProvider.skillsProvidersProviderIdGET(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersProviderIdHEAD = function skillsProvidersProviderIdHEAD (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - SkillsProvider.skillsProvidersProviderIdHEAD(providerId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.skillsProvidersProviderIdPATCH = function skillsProvidersProviderIdPATCH (req, res, next) { - var providerId = req.swagger.params['providerId'].value; - var body = req.swagger.params['body'].value; - SkillsProvider.skillsProvidersProviderIdPATCH(providerId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/UserAttributes.js b/controllers/UserAttributes.js deleted file mode 100644 index 8add563..0000000 --- a/controllers/UserAttributes.js +++ /dev/null @@ -1,93 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var UserAttributes = require('../service/UserAttributesService'); - -module.exports.usersUserIdAttributesAttributeIdDELETE = function usersUserIdAttributesAttributeIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeId = req.swagger.params['attributeId'].value; - UserAttributes.usersUserIdAttributesAttributeIdDELETE(userId,attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesAttributeIdGET = function usersUserIdAttributesAttributeIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeId = req.swagger.params['attributeId'].value; - UserAttributes.usersUserIdAttributesAttributeIdGET(userId,attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesAttributeIdHEAD = function usersUserIdAttributesAttributeIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeId = req.swagger.params['attributeId'].value; - UserAttributes.usersUserIdAttributesAttributeIdHEAD(userId,attributeId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesAttributeIdPATCH = function usersUserIdAttributesAttributeIdPATCH (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeId = req.swagger.params['attributeId'].value; - var body = req.swagger.params['body'].value; - UserAttributes.usersUserIdAttributesAttributeIdPATCH(userId,attributeId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesGET = function usersUserIdAttributesGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeName = req.swagger.params['attributeName'].value; - var attributeGroupName = req.swagger.params['attributeGroupName'].value; - var attributeGroupId = req.swagger.params['attributeGroupId'].value; - UserAttributes.usersUserIdAttributesGET(userId,attributeName,attributeGroupName,attributeGroupId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesHEAD = function usersUserIdAttributesHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var attributeName = req.swagger.params['attributeName'].value; - var attributeGroupName = req.swagger.params['attributeGroupName'].value; - var attributeGroupId = req.swagger.params['attributeGroupId'].value; - UserAttributes.usersUserIdAttributesHEAD(userId,attributeName,attributeGroupName,attributeGroupId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdAttributesPOST = function usersUserIdAttributesPOST (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - UserAttributes.usersUserIdAttributesPOST(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/UserRoles.js b/controllers/UserRoles.js deleted file mode 100644 index e9e3f80..0000000 --- a/controllers/UserRoles.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var UserRoles = require('../service/UserRolesService'); - -module.exports.usersUserIdRolesGET = function usersUserIdRolesGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - UserRoles.usersUserIdRolesGET(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdRolesHEAD = function usersUserIdRolesHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - UserRoles.usersUserIdRolesHEAD(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdRolesPOST = function usersUserIdRolesPOST (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - UserRoles.usersUserIdRolesPOST(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdRolesRoleIdDELETE = function usersUserIdRolesRoleIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - var roleId = req.swagger.params['roleId'].value; - UserRoles.usersUserIdRolesRoleIdDELETE(userId,roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdRolesRoleIdGET = function usersUserIdRolesRoleIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var roleId = req.swagger.params['roleId'].value; - UserRoles.usersUserIdRolesRoleIdGET(userId,roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdRolesRoleIdHEAD = function usersUserIdRolesRoleIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var roleId = req.swagger.params['roleId'].value; - UserRoles.usersUserIdRolesRoleIdHEAD(userId,roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/Users.js b/controllers/Users.js deleted file mode 100644 index 7bb8453..0000000 --- a/controllers/Users.js +++ /dev/null @@ -1,86 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var Users = require('../service/UsersService'); - -module.exports.usersGET = function usersGET (req, res, next) { - var handle = req.swagger.params['handle'].value; - var groupId = req.swagger.params['groupId'].value; - var roleId = req.swagger.params['roleId'].value; - Users.usersGET(handle,groupId,roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersHEAD = function usersHEAD (req, res, next) { - var handle = req.swagger.params['handle'].value; - var groupId = req.swagger.params['groupId'].value; - var roleId = req.swagger.params['roleId'].value; - Users.usersHEAD(handle,groupId,roleId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersPOST = function usersPOST (req, res, next) { - var body = req.swagger.params['body'].value; - Users.usersPOST(body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdDELETE = function usersUserIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - Users.usersUserIdDELETE(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdGET = function usersUserIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - Users.usersUserIdGET(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdHEAD = function usersUserIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - Users.usersUserIdHEAD(userId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdPATCH = function usersUserIdPATCH (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - Users.usersUserIdPATCH(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/controllers/UsersSkills.js b/controllers/UsersSkills.js deleted file mode 100644 index 466df91..0000000 --- a/controllers/UsersSkills.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -var utils = require('../utils/writer.js'); -var UsersSkills = require('../service/UsersSkillsService'); - -module.exports.usersUserIdSkillsGET = function usersUserIdSkillsGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillName = req.swagger.params['skillName'].value; - UsersSkills.usersUserIdSkillsGET(userId,skillName) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsHEAD = function usersUserIdSkillsHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillName = req.swagger.params['skillName'].value; - UsersSkills.usersUserIdSkillsHEAD(userId,skillName) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsPOST = function usersUserIdSkillsPOST (req, res, next) { - var userId = req.swagger.params['userId'].value; - var body = req.swagger.params['body'].value; - UsersSkills.usersUserIdSkillsPOST(userId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsSkillIdDELETE = function usersUserIdSkillsSkillIdDELETE (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillId = req.swagger.params['skillId'].value; - UsersSkills.usersUserIdSkillsSkillIdDELETE(userId,skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsSkillIdGET = function usersUserIdSkillsSkillIdGET (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillId = req.swagger.params['skillId'].value; - UsersSkills.usersUserIdSkillsSkillIdGET(userId,skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsSkillIdHEAD = function usersUserIdSkillsSkillIdHEAD (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillId = req.swagger.params['skillId'].value; - UsersSkills.usersUserIdSkillsSkillIdHEAD(userId,skillId) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; - -module.exports.usersUserIdSkillsSkillIdPATCH = function usersUserIdSkillsSkillIdPATCH (req, res, next) { - var userId = req.swagger.params['userId'].value; - var skillId = req.swagger.params['skillId'].value; - var body = req.swagger.params['body'].value; - UsersSkills.usersUserIdSkillsSkillIdPATCH(userId,skillId,body) - .then(function (response) { - utils.writeJson(res, response); - }) - .catch(function (response) { - utils.writeJson(res, response); - }); -}; diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json new file mode 100644 index 0000000..b445a55 --- /dev/null +++ b/docs/UBahn_API.postman_collection.json @@ -0,0 +1,3104 @@ +{ + "info": { + "_postman_id": "67870cba-bdd9-4825-9f61-b747ae6d4757", + "name": "UBahn_API", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "users", + "item": [ + { + "name": "{{HOST}}/users", + "event": [ + { + "listen": "test", + "script": { + "id": "9be82782-7a85-4db7-b4f3-aa3f739abb85", + "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": { + "language": "json" + } + } + }, + "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}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "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": { + "language": "json" + } + } + }, + "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?handle=h&roleId=8607ddb3-abf6-4512-a618-c60d4771174b", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ], + "query": [ + { + "key": "handle", + "value": "h" + }, + { + "key": "roleId", + "value": "8607ddb3-abf6-4512-a618-c60d4771174b" + } + ] + } + }, + "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" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "roles", + "item": [ + { + "name": "{{HOST}}/roles", + "event": [ + { + "listen": "test", + "script": { + "id": "ff8c198b-a5fc-421c-8930-ff4f7ac2cc29", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "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": "aba26e50-293b-482e-b023-47adba0f4ff3", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "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": "65a0007b-379b-42f6-801e-cdd2d195a586", + "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": { + "language": "json" + } + } + }, + "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/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "skillsProviders", + "item": [ + { + "name": "{{HOST}}/skillsProviders", + "event": [ + { + "listen": "test", + "script": { + "id": "624175cd-8315-42a3-a316-32a34fbcfadf", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "skills", + "item": [ + { + "name": "{{HOST}}/skills", + "event": [ + { + "listen": "test", + "script": { + "id": "c7176c3b-5fea-4236-96cb-cfd7ff1383ee", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + ] + } + }, + "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" + } + ], + "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": "c1c418d7-bcda-4fd0-95ec-62050e002ea7", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ], + "query": [ + { + "key": "skillName", + "value": "ju", + "disabled": true + } + ] + } + }, + "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" + } + ], + "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": "84cfec46-193e-4705-9f37-2f1cfae42cd7", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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=o", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ], + "query": [ + { + "key": "organizationName", + "value": "o" + } + ] + } + }, + "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" + } + ], + "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": "d52dbd5e-7b35-42c9-b306-6dade4498eec", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "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": "4e1eecd6-6e64-4e1c-a3bb-b02430030fe2", + "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": { + "language": "json" + } + } + }, + "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/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "{{achievementsProviderId}}" + ] + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "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": "bedc5acf-f088-400c-8b89-8f1ae915b0e0", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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?name=roup", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ], + "query": [ + { + "key": "name", + "value": "roup" + }, + { + "key": "organizationId", + "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "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" + } + ], + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + }, + { + "name": "attributes", + "item": [ + { + "name": "{{HOST}}/attributes", + "event": [ + { + "listen": "test", + "script": { + "id": "6b14dfd2-8f67-4806-8c55-b468e78cbf52", + "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": { + "language": "json" + } + } + }, + "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": { + "language": "json" + } + } + }, + "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" + } + ], + "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": "f7a05416-fcef-4107-9c35-e83eccbc6f07", + "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": { + "language": "json" + } + } + }, + "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/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } + }, + "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": { + "language": "json" + } + } + }, + "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=attr&attributeGroupName=roup", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ], + "query": [ + { + "key": "attributeName", + "value": "attr" + }, + { + "key": "attributeGroupName", + "value": "roup" + }, + { + "key": "attributeGroupId", + "value": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", + "disabled": true + } + ] + } + }, + "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" + } + ], + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } + }, + "response": [] + } + ], + "protocolProfileBehavior": {} + } + ], + "protocolProfileBehavior": {} +} diff --git a/docs/UBahn_ENV.postman_environment.json b/docs/UBahn_ENV.postman_environment.json new file mode 100644 index 0000000..6ae77ae --- /dev/null +++ b/docs/UBahn_ENV.postman_environment.json @@ -0,0 +1,59 @@ +{ + "id": "5ceec1fa-02fe-4556-be97-40e279ad003a", + "name": "UBahn_ENV", + "values": [ + { + "key": "HOST", + "value": "http://127.0.0.1:3001/api/1.0", + "enabled": true + }, + { + "key": "token", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLXVzZXIiLCJleHAiOjE2ODU1NzE0NjAsInVzZXJJZCI6IjIzMTY2NzY2IiwiaWF0IjoxNTg1NTcwODYwLCJlbWFpbCI6InRjLXVzZXJAZ21haWwuY29tIiwianRpIjoiMGYxZWYxZDMtMmIzMy00OTAwLWJiNDMtNDhmMjI4NWY5NjI3In0.eBhXqSBe8zMRg2nBeGeZDgKiJdAYs0zOMzGfJCjWfcs", + "enabled": true + }, + { + "key": "userId", + "value": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "enabled": true + }, + { + "key": "roleId", + "value": "", + "enabled": true + }, + { + "key": "skillsProviderId", + "value": "", + "enabled": true + }, + { + "key": "skillId", + "value": "", + "enabled": true + }, + { + "key": "organizationId", + "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "enabled": true + }, + { + "key": "achievementsProviderId", + "value": "eb327c00-0090-45af-96d2-593408c96397", + "enabled": true + }, + { + "key": "attributeGroupId", + "value": "84634bbd-8191-40cf-a03e-9962d7e39fda", + "enabled": true + }, + { + "key": "attributeId", + "value": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2020-05-13T08:36:10.472Z", + "_postman_exported_using": "Postman/7.24.0" +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index b8fe33d..fb774fd 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -6,7 +6,7 @@ info: \ and expertise for suitability in other projects" version: "1.0.0" title: "UBahn API" -host: "api.ubahn-dev.com" +host: "ubahn-api-dev.herokuapp.com" basePath: "/v5" tags: - name: "Users" @@ -2436,7 +2436,7 @@ paths: head: tags: - "User Attributes" - description: "Retrieve header information for a search operation on users 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" diff --git a/index.js b/index.js deleted file mode 100644 index ace12f9..0000000 --- a/index.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict'; - -var fs = require('fs'), - path = require('path'), - http = require('http'), - cors = require('cors'); - -var app = require('connect')(); -var swaggerTools = require('swagger-tools'); -var jsyaml = require('js-yaml'); -var serverPort = process.env.PORT || 8080; - -// swaggerRouter configuration -var options = { - swaggerUi: path.join(__dirname, '/swagger.json'), - controllers: path.join(__dirname, './controllers'), - useStubs: process.env.NODE_ENV === 'development' // Conditionally turn on stubs (mock mode) -}; - -// The Swagger document (require it, build it programmatically, fetch it from a URL, ...) -var spec = fs.readFileSync(path.join(__dirname,'docs/swagger.yaml'), 'utf8'); -var swaggerDoc = jsyaml.safeLoad(spec); - -app.use(cors()) - -// Initialize the Swagger middleware -swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) { - - // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain - app.use(middleware.swaggerMetadata()); - - // Validate Swagger requests - app.use(middleware.swaggerValidator()); - - // Route validated requests to appropriate controller - app.use(middleware.swaggerRouter(options)); - - // Serve the Swagger documents and Swagger UI - app.use(middleware.swaggerUi()); - - // Start the server - http.createServer(app).listen(serverPort, function () { - console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort); - console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort); - }); - -}); diff --git a/package-lock.json b/package-lock.json index 22ac14e..0c63726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,10 +4,242 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" + "@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.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", + "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "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.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz", + "integrity": "sha512-578YH5Lt88AKoADy0b2jQGwJtrBxezXtVe/MBqWXKZpqx91SnC0pVkVCcxcytz3lWW+cHBYDi3Ysh0WXc+rAYw==", + "requires": { + "@types/node": "*", + "@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.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" + }, + "@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.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz", + "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==" + }, + "@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.3", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", + "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "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 + }, + "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", @@ -17,6 +249,41 @@ "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", @@ -30,71 +297,245 @@ "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.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + }, + "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.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "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.0.0", + "bytes": "3.1.0", "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - } + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" } }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + "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" + } }, - "busboy": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", - "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", + "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.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "requires": { - "dicer": "0.2.5", - "readable-stream": "1.1.x" + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "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 + }, + "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": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + "semver": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", + "integrity": "sha1-n7P0AE+QDYPEeWj+QvdYPgWDLMk=" } } }, - "bytes": { + "color": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + "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", @@ -104,36 +545,31 @@ "delayed-stream": "~1.0.0" } }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" - }, - "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=" }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "config": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/config/-/config-3.3.1.tgz", + "integrity": "sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==", "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "json5": "^2.1.1" } }, - "connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "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": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" + "safe-buffer": "5.1.2" } }, "content-type": { @@ -141,10 +577,15 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, - "cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + "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=" }, "core-js": { "version": "2.6.11", @@ -165,6 +606,27 @@ "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", @@ -173,6 +635,63 @@ "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", @@ -188,36 +707,49 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "dicer": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", - "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", + "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": { - "readable-stream": "1.1.x", - "streamsearch": "0.1.2" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" - } + "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": { @@ -225,31 +757,496 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "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=", + "dev": true + }, + "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", @@ -264,60 +1261,494 @@ "unpipe": "~1.0.0" } }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "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.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" + } + }, + "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-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 + }, + "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-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" + } + }, + "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": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "jsbi": "^3.1.1" } }, - "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==" + "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==" }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + "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==" }, - "graphlib": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", - "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "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": { - "lodash": "^4.17.15" + "is-extglob": "^2.1.1" } }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "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": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "has": "^1.0.3" } }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "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=" }, + "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=" + }, + "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", @@ -327,265 +1758,278 @@ "esprima": "^4.0.0" } }, - "json-refs": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-3.0.15.tgz", - "integrity": "sha512-0vOQd9eLNBL18EGl5yYaO44GhixmImes2wiYn9Z3sag3QnehWrYWlB9AFtMxCL2Bj3fyxgDYkxGFEU/chlYssw==", - "requires": { - "commander": "~4.1.1", - "graphlib": "^2.1.8", - "js-yaml": "^3.13.1", - "lodash": "^4.17.15", - "native-promise-only": "^0.8.1", - "path-loader": "^1.0.10", - "slash": "^3.0.0", - "uri-js": "^4.2.2" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - } - } + "jsbi": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.2.tgz", + "integrity": "sha512-5nDXo1X9QVaXK/Cpb5VECV9ss1QPbjUuk1qSruHB1PK/g39Sd414K4nci99ElFDZv0vzxDEnKn3o49/Tn9Yagw==" }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, - "lodash._arraypool": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._arraypool/-/lodash._arraypool-2.4.1.tgz", - "integrity": "sha1-6I7suS4ruEyQZWEv2VigcZzUf5Q=" + "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 }, - "lodash._basebind": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.4.1.tgz", - "integrity": "sha1-6UC5690nwyfgqNqxtVkWxTQelXU=", - "requires": { - "lodash._basecreate": "~2.4.1", - "lodash._setbinddata": "~2.4.1", - "lodash._slice": "~2.4.1", - "lodash.isobject": "~2.4.1" - } + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, - "lodash._baseclone": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._baseclone/-/lodash._baseclone-2.4.1.tgz", - "integrity": "sha1-MPgj5X4X43NdODvWK2Czh1Q7QYY=", - "requires": { - "lodash._getarray": "~2.4.1", - "lodash._releasearray": "~2.4.1", - "lodash._slice": "~2.4.1", - "lodash.assign": "~2.4.1", - "lodash.foreach": "~2.4.1", - "lodash.forown": "~2.4.1", - "lodash.isarray": "~2.4.1", - "lodash.isobject": "~2.4.1" - } + "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==" }, - "lodash._basecreate": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-2.4.1.tgz", - "integrity": "sha1-+Ob1tXip405UEXm1a47uv0oofgg=", + "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": { - "lodash._isnative": "~2.4.1", - "lodash.isobject": "~2.4.1", - "lodash.noop": "~2.4.1" + "minimist": "^1.2.5" } }, - "lodash._basecreatecallback": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._basecreatecallback/-/lodash._basecreatecallback-2.4.1.tgz", - "integrity": "sha1-fQsmdknLKeehOdAQO3wR+uhOSFE=", + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", "requires": { - "lodash._setbinddata": "~2.4.1", - "lodash.bind": "~2.4.1", - "lodash.identity": "~2.4.1", - "lodash.support": "~2.4.1" + "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==" + } } }, - "lodash._basecreatewrapper": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._basecreatewrapper/-/lodash._basecreatewrapper-2.4.1.tgz", - "integrity": "sha1-TTHy595+E0+/KAN2K4FQsyUZZm8=", + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "requires": { - "lodash._basecreate": "~2.4.1", - "lodash._setbinddata": "~2.4.1", - "lodash._slice": "~2.4.1", - "lodash.isobject": "~2.4.1" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" } }, - "lodash._createwrapper": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._createwrapper/-/lodash._createwrapper-2.4.1.tgz", - "integrity": "sha1-UdaVeXPaTtVW43KQ2MGhjFPeFgc=", + "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": { - "lodash._basebind": "~2.4.1", - "lodash._basecreatewrapper": "~2.4.1", - "lodash._slice": "~2.4.1", - "lodash.isfunction": "~2.4.1" + "array-includes": "^3.0.3", + "object.assign": "^4.1.0" } }, - "lodash._getarray": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._getarray/-/lodash._getarray-2.4.1.tgz", - "integrity": "sha1-+vH3+BD6mFolHCGHQESBCUg55e4=", + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "requires": { - "lodash._arraypool": "~2.4.1" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "lodash._isnative": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._isnative/-/lodash._isnative-2.4.1.tgz", - "integrity": "sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw=" - }, - "lodash._maxpoolsize": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._maxpoolsize/-/lodash._maxpoolsize-2.4.1.tgz", - "integrity": "sha1-nUgvRjuOZq++WcLBTtsRcGAXIzQ=" - }, - "lodash._objecttypes": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz", - "integrity": "sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE=" - }, - "lodash._releasearray": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._releasearray/-/lodash._releasearray-2.4.1.tgz", - "integrity": "sha1-phOWMNdtFTawfdyAliiJsIL2pkE=", + "jwks-rsa": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.8.0.tgz", + "integrity": "sha512-+HYROHD5fsYQCNrJ37RSr2NjbN2/V9YT+yVF3oJxLmPIZWrmp1SOl1hMw2RcuNh+LGA2bGZIhRKGiMjhQa/b7Q==", "requires": { - "lodash._arraypool": "~2.4.1", - "lodash._maxpoolsize": "~2.4.1" + "@types/express-jwt": "0.0.42", + "axios": "^0.19.2", + "debug": "^4.1.0", + "jsonwebtoken": "^8.5.1", + "limiter": "^1.1.4", + "lru-memoizer": "^2.0.1", + "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==" + } } }, - "lodash._setbinddata": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._setbinddata/-/lodash._setbinddata-2.4.1.tgz", - "integrity": "sha1-98IAzRuS7yNrOZ7s9zxkjReqlNI=", + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "requires": { - "lodash._isnative": "~2.4.1", - "lodash.noop": "~2.4.1" + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" } }, - "lodash._shimkeys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz", - "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", "requires": { - "lodash._objecttypes": "~2.4.1" + "colornames": "^1.1.1" } }, - "lodash._slice": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash._slice/-/lodash._slice-2.4.1.tgz", - "integrity": "sha1-dFz0GlNZexj2iImFREBe+isG2Q8=" - }, - "lodash.assign": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-2.4.1.tgz", - "integrity": "sha1-hMOVlt1xGBqXsGUpE6fJZ15Jsao=", + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, "requires": { - "lodash._basecreatecallback": "~2.4.1", - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, - "lodash.bind": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-2.4.1.tgz", - "integrity": "sha1-XRn6AFyMTSNvr0dCx7eh/Kvikmc=", - "requires": { - "lodash._createwrapper": "~2.4.1", - "lodash._slice": "~2.4.1" - } + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, - "lodash.clonedeep": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-2.4.1.tgz", - "integrity": "sha1-8pIDtAsS/uCkXTYxZIJZvrq8eGg=", + "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": { - "lodash._baseclone": "~2.4.1", - "lodash._basecreatecallback": "~2.4.1" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" } }, - "lodash.foreach": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-2.4.1.tgz", - "integrity": "sha1-/j/Do0yGyUyrb5UiVgKCdB4BYwk=", + "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": { - "lodash._basecreatecallback": "~2.4.1", - "lodash.forown": "~2.4.1" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, - "lodash.forown": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.forown/-/lodash.forown-2.4.1.tgz", - "integrity": "sha1-eLQer+FAX6lmRZ6kGT/VAtCEUks=", - "requires": { - "lodash._basecreatecallback": "~2.4.1", - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" - } + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + "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.identity": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-2.4.1.tgz", - "integrity": "sha1-ZpTP+mX++TH3wxzobHRZfPVg9PE=" + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "lodash.isarray": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-2.4.1.tgz", - "integrity": "sha1-tSoybB9i9tfac6MdVAHfbvRPD6E=", + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", "requires": { - "lodash._isnative": "~2.4.1" + "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==" + } } }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" - }, - "lodash.isfunction": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-2.4.1.tgz", - "integrity": "sha1-LP1XXHPkmKtX4xm3f6Aq3vE6lNE=" - }, - "lodash.isobject": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-2.4.1.tgz", - "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", + "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": { - "lodash._objecttypes": "~2.4.1" + "js-tokens": "^3.0.0 || ^4.0.0" } }, - "lodash.keys": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-2.4.1.tgz", - "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=", "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" } }, - "lodash.noop": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-2.4.1.tgz", - "integrity": "sha1-T7VPgWZS5a4Q6PcvcXo4jHMmU4o=" - }, - "lodash.support": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/lodash.support/-/lodash.support-2.4.1.tgz", - "integrity": "sha1-Mg4LZwMWc8KNeiu12eAzGkUkBRU=", + "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._isnative": "~2.4.1" + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" } }, "media-typer": { @@ -593,11 +2037,21 @@ "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", @@ -616,6 +2070,20 @@ "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", @@ -629,77 +2097,485 @@ "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=" }, - "multer": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.2.tgz", - "integrity": "sha512-xY8pX7V+ybyUpbYMxtjM9KAiD9ixtg5/JkeKUTD6xilfDv0vzzOFcCp4Ljb1UU3tSOM3VTZtKo63OmzOrGi3Cg==", + "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": { - "append-field": "^1.0.0", - "busboy": "^0.2.11", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.1", - "on-finished": "^2.3.0", - "type-is": "^1.6.4", - "xtend": "^4.0.0" + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" } }, - "native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=" + "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 + }, + "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=" }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "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": { - "ee-first": "1.1.1" + "debug-log": "^1.0.0", + "find-root": "^1.0.0", + "xtend": "^4.0.1" } }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "path-loader": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.10.tgz", - "integrity": "sha512-CMP0v6S6z8PHeJ6NFVyVJm6WyJjIwFvyz2b0n2/4bKdS/0uZa/9sKUlYZzubrn3zuDRU0zIuEDX9DZYQ2ZI8TA==", + "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": { - "native-promise-only": "^0.8.1", - "superagent": "^3.8.3" + "find-up": "^2.1.0" } }, - "path-to-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", - "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==" + "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": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, "qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + "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": { + "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", @@ -707,51 +2583,159 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "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.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", + "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": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - } + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, - "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==", + "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": { - "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" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -759,6 +2743,32 @@ "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", @@ -779,32 +2789,10 @@ "statuses": "~1.5.0" }, "dependencies": { - "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "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==" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" } } }, @@ -820,131 +2808,416 @@ } }, "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + "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 + } + } }, - "slash": { + "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/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "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" + } }, - "spark-md5": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.1.tgz", - "integrity": "sha512-0tF3AGSD1ppQeuffsLDIOWlKUd3lS92tFxcsrh5Pe3ZphhnoK+oXIBTzOAThZCiuINZLvpiLH/1VS1/ANEJVig==" + "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=" }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "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" - } - }, - "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" + "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": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "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": { - "ms": "^2.1.1" + "ansi-regex": "^5.0.0" } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, - "swagger-converter": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/swagger-converter/-/swagger-converter-0.1.7.tgz", - "integrity": "sha1-oJdRnG8e5N1n4wjZtT3cnCslf5c=", - "requires": { - "lodash.clonedeep": "^2.4.1" - } - }, - "swagger-tools": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/swagger-tools/-/swagger-tools-0.10.4.tgz", - "integrity": "sha512-VQpijIi8cpB/frUZOZlVpS7U3CrdSAZBfiHu448R1njiNXUnE7heF3Svz3qFBr5SYtaPvaqWpHMbvboirCXVzA==", - "requires": { - "async": "^2.5.0", - "body-parser": "1.18.2", - "commander": "~2.11.0", - "debug": "^3.1.0", - "js-yaml": "^3.3.1", - "json-refs": "^3.0.2", - "lodash": "^4.17.4", - "multer": "^1.1.0", - "parseurl": "^1.3.0", - "path-to-regexp": "^2.0.0", - "qs": "^6.0.3", - "serve-static": "^1.10.0", - "spark-md5": "^3.0.0", - "superagent": "^3.5.2", - "swagger-converter": "^0.1.7", - "traverse": "^0.6.6", - "z-schema": "^3.15.4" + "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": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "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 + }, + "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": { - "ms": "^2.1.1" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, + "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==" }, - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + "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", @@ -955,10 +3228,11 @@ "mime-types": "~2.1.24" } }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + "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", @@ -971,6 +3245,22 @@ "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": { @@ -983,32 +3273,144 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "validator": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", - "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + "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==" + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true }, - "z-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.25.1.tgz", - "integrity": "sha512-7tDlwhrBG+oYFdXNOjILSurpfQyuVgkRe3hB2q8TEssamDHB7BbLWYkYO98nTn0FibfdFroFKDjndbgufAgS/Q==", - "requires": { - "commander": "^2.7.1", - "core-js": "^2.5.7", - "lodash.get": "^4.0.0", - "lodash.isequal": "^4.0.0", - "validator": "^10.0.0" - } + "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 old mode 100644 new mode 100755 index a3a643d..5e38f26 --- a/package.json +++ b/package.json @@ -1,21 +1,39 @@ { "name": "ubahn-api", "version": "1.0.0", - "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", - "main": "index.js", + "description": "UBahn api", + "main": "app.js", "scripts": { - "prestart": "npm install", - "start": "node index.js" + "start": "node app.js", + "lint": "standard \"**/*.js\"" + }, + "repository": { + "type": "git", + "url": "" }, - "keywords": [ - "swagger" - ], - "license": "Unlicense", - "private": true, "dependencies": { - "connect": "^3.2.0", + "@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", + "body-parser": "^1.19.0", + "config": "^3.2.4", "cors": "^2.8.5", - "js-yaml": "^3.3.0", - "swagger-tools": "^0.10.4" + "express": "^4.17.1", + "get-parameter-names": "^0.3.0", + "ion-js": "^3.1.2", + "js-yaml": "^3.13.1", + "lodash": "^4.17.15", + "moment": "^2.24.0", + "swagger-ui-express": "^4.1.4", + "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/db/data/Achievement.json b/scripts/db/data/Achievement.json new file mode 100644 index 0000000..6e6ea57 --- /dev/null +++ b/scripts/db/data/Achievement.json @@ -0,0 +1,28 @@ +[ + { + "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": "string", + "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": "achievement-name-01", + "uri": "http://www.google.com/xx", + "certifierId": "certifierId", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + } +] \ No newline at end of file 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..198a15e --- /dev/null +++ b/scripts/db/data/Attribute.json @@ -0,0 +1,29 @@ +[ + { + "id": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", + "created": "2020-05-13T08:46:39.030Z", + "updated": null, + "createdBy": "tc-user", + "updatedBy": null, + "attributeGroupId": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", + "name": "attribute_02" + }, + { + "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": "attribute_02" + }, + { + "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": "attribute_01" + } +] \ No newline at end of file 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..1aaee50 --- /dev/null +++ b/scripts/db/data/ExternalProfile.json @@ -0,0 +1,12 @@ +[ + { + "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", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "uri": "http://www.new.com/new-uri" + } +] \ No newline at end of file diff --git a/scripts/db/data/Organization.json b/scripts/db/data/Organization.json new file mode 100644 index 0000000..5811c91 --- /dev/null +++ b/scripts/db/data/Organization.json @@ -0,0 +1,10 @@ +[ + { + "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": "organization_update" + } +] \ No newline at end of file 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..75e8860 --- /dev/null +++ b/scripts/db/data/Skill.json @@ -0,0 +1,13 @@ +[ + { + "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": "skill_name_update", + "externalId": "externalId", + "uri": "http://www.google.com" + } +] \ No newline at end of file diff --git a/scripts/db/data/SkillsProvider.json b/scripts/db/data/SkillsProvider.json new file mode 100644 index 0000000..1e64a84 --- /dev/null +++ b/scripts/db/data/SkillsProvider.json @@ -0,0 +1,18 @@ +[ + { + "id": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", + "created": "2020-05-05T11:02:49.718Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "name": "skillsProviders_01" + }, + { + "id": "77003860-3c24-4438-84e7-85ab8a4358d6", + "created": "2020-05-05T11:02:32.932Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "name": "skillsProviders_01" + } +] \ No newline at end of file diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json new file mode 100644 index 0000000..ed19fde --- /dev/null +++ b/scripts/db/data/User.json @@ -0,0 +1,18 @@ +[ + { + "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": "handle_05" + }, + { + "id": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699", + "created": "2020-05-05T10:18:03.882Z", + "updated": null, + "createdBy": "tc-Copilot", + "updatedBy": null, + "handle": "handle_01" + } +] \ No newline at end of file diff --git a/scripts/db/data/UsersAttribute.json b/scripts/db/data/UsersAttribute.json new file mode 100644 index 0000000..e196b57 --- /dev/null +++ b/scripts/db/data/UsersAttribute.json @@ -0,0 +1,22 @@ +[ + { + "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": "1.23", + "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": "1.23", + "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" + } +] \ No newline at end of file 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..e8c4623 --- /dev/null +++ b/scripts/db/data/UsersSkill.json @@ -0,0 +1,14 @@ +[ + { + "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" + } +] \ No newline at end of file diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js new file mode 100644 index 0000000..eb2426f --- /dev/null +++ b/scripts/db/dropAll.js @@ -0,0 +1,24 @@ +/** + * drop tables + */ +const models = require('../../src/models') +const logger = require('../../src/common/logger') + +async function main () { + const keys = Object.keys(models) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + if (models[key].tableName) { + try { + await models.DBHelper.drop(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..146112d --- /dev/null +++ b/scripts/db/genData.js @@ -0,0 +1,33 @@ +const models = require('../../src/models') +const logger = require('../../src/common/logger') + +/** + * import test data + * @return {Promise} + */ +async function main () { + await models.init() + const keys = Object.keys(models) + 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) + } + 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/service/AchievementsProviderService.js b/service/AchievementsProviderService.js deleted file mode 100644 index be9dfc8..0000000 --- a/service/AchievementsProviderService.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - - -/** - * Search Achievements Provider in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by provider name (optional) - * returns List - **/ -exports.achievementsProvidersGET = function(name) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on achivements provider in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by provider name (optional) - * no response value expected for this operation - **/ -exports.achievementsProvidersHEAD = function(name) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Achievements Provider. **Security** - This endpoint is accessible by all authenticated users. - * - * body NameRequestBody - * returns AchievementsProvider - **/ -exports.achievementsProvidersPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing achiements provider with given id. **Security** - Note that this endpoint is only available for admin users. - * - * providerId String The provider id - * no response value expected for this operation - **/ -exports.achievementsProvidersProviderIdDELETE = function(providerId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get achievements provider with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * providerId String The provider id - * returns AchievementsProvider - **/ -exports.achievementsProvidersProviderIdGET = function(providerId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get achivements provider with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * providerId String The provider id - * no response value expected for this operation - **/ -exports.achievementsProvidersProviderIdHEAD = function(providerId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing achivements provider with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * providerId String The provider id - * body NameRequestBody - * returns AchievementsProvider - **/ -exports.achievementsProvidersProviderIdPATCH = function(providerId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/AchievementsService.js b/service/AchievementsService.js deleted file mode 100644 index 37ce51a..0000000 --- a/service/AchievementsService.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; - - -/** - * Remove an existing Achievement with given userId and achievement providerId. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * achievementsProviderId String The provider id - * no response value expected for this operation - **/ -exports.usersUserIdAchievementsAchievementsProviderIdDELETE = function(userId,achievementsProviderId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get Achievements for given user id and provider id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * achievementsProviderId String The provider id - * returns List - **/ -exports.usersUserIdAchievementsAchievementsProviderIdGET = function(userId,achievementsProviderId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get Achievements for given user id and provider id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * achievementsProviderId String The provider id - * no response value expected for this operation - **/ -exports.usersUserIdAchievementsAchievementsProviderIdHEAD = function(userId,achievementsProviderId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing Achievement with given userId and achievement providerId. Only the fields in the request body are updated. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * userId String The user id - * achievementsProviderId String The provider id - * body AchievementUpdateRequestBody - * returns Achievement - **/ -exports.usersUserIdAchievementsAchievementsProviderIdPATCH = function(userId,achievementsProviderId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get Achievements for given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * achievementsproviderName String The achievement provider name (optional) - * returns List - **/ -exports.usersUserIdAchievementsGET = function(userId,achievementsproviderName) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get Achievements for given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * achievementsproviderName String The achievement provider name (optional) - * no response value expected for this operation - **/ -exports.usersUserIdAchievementsHEAD = function(userId,achievementsproviderName) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Achievement. **Security** - This endpoint is accessible by all authenticated users. - * - * userId String The user id - * body AchievementRequestBody - * returns Achievement - **/ -exports.usersUserIdAchievementsPOST = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/AttributeGroupsService.js b/service/AttributeGroupsService.js deleted file mode 100644 index badf0f0..0000000 --- a/service/AttributeGroupsService.js +++ /dev/null @@ -1,120 +0,0 @@ -'use strict'; - - -/** - * Search Attribute Groups in the application. Multiple filters are supported. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by group name (optional) - * organizationId String Filter by organization id (optional) - * returns List - **/ -exports.attributeGroupsGET = function(name,organizationId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on Attribute Groups in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by group name (optional) - * organizationId String Filter by organization id (optional) - * no response value expected for this operation - **/ -exports.attributeGroupsHEAD = function(name,organizationId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Remove an existing Attribute Group with given id. **Security** - Note that this endpoint is only available for admin users. - * - * id String The id - * no response value expected for this operation - **/ -exports.attributeGroupsIdDELETE = function(id) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get Attribute Groups with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * id String The id - * returns AttributeGroup - **/ -exports.attributeGroupsIdGET = function(id) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get Attribute Group with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * id String The id - * no response value expected for this operation - **/ -exports.attributeGroupsIdHEAD = function(id) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing Attribute Group with given id. Only the fields in the request body are updated. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * id String The id - * body AttributeGroupRequestBody - * returns AttributeGroup - **/ -exports.attributeGroupsIdPATCH = function(id,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Create a new Attribute Group. **Security** - This endpoint is accessible by all authenticated users. - * - * body AttributeGroupRequestBody - * returns AttributeGroup - **/ -exports.attributeGroupsPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/AttributesService.js b/service/AttributesService.js deleted file mode 100644 index 41cec61..0000000 --- a/service/AttributesService.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - - -/** - * Remove an existing attribute with given id. **Security** - Note that this endpoint is only available for admin users. - * - * attributeId String The attribute id - * no response value expected for this operation - **/ -exports.attributesAttributeIdDELETE = function(attributeId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get Attribute by given attribute id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * attributeId String The attribute id - * returns Attribute - **/ -exports.attributesAttributeIdGET = function(attributeId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for get operation on Attribute by its id in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * attributeId String The attribute id - * no response value expected for this operation - **/ -exports.attributesAttributeIdHEAD = function(attributeId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing attribute with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * attributeId String The attribute id - * body AttributeUpdateRequestBody - * returns Attribute - **/ -exports.attributesAttributeIdPATCH = function(attributeId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get list of attributes in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * attributeGroupId String Filter by attribute group id (optional) - * name String Filter by attribute name (optional) - * returns List - **/ -exports.attributesGET = function(attributeGroupId,name) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for get operation on Attributes in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * no response value expected for this operation - **/ -exports.attributesHEAD = function() { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Attribute. **Security** - This endpoint is accessible by all authenticated users. - * - * body AttributeRequestBody - * returns Attribute - **/ -exports.attributesPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/ExternalProfilesService.js b/service/ExternalProfilesService.js deleted file mode 100644 index cd6d5d7..0000000 --- a/service/ExternalProfilesService.js +++ /dev/null @@ -1,124 +0,0 @@ -'use strict'; - - -/** - * Get External Profiles with given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * organizationName String The organization name (optional) - * returns List - **/ -exports.usersUserIdExternalProfilesGET = function(userId,organizationName) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get External Profiles with given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * no response value expected for this operation - **/ -exports.usersUserIdExternalProfilesHEAD = function(userId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Remove an existing external profile with given user id and organization id. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * organizationId String The organization id - * no response value expected for this operation - **/ -exports.usersUserIdExternalProfilesOrganizationIdDELETE = function(userId,organizationId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get external profile with given user id and organization id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * organizationId String The organization id - * returns ExternalProfile - **/ -exports.usersUserIdExternalProfilesOrganizationIdGET = function(userId,organizationId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get external profile with given user id and organization id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * organizationId String The organization id - * no response value expected for this operation - **/ -exports.usersUserIdExternalProfilesOrganizationIdHEAD = function(userId,organizationId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing external profile with given user id and organization id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * userId String The user id - * organizationId String The organization id - * body ExternalProfileUpdateRequestBody - * returns ExternalProfile - **/ -exports.usersUserIdExternalProfilesOrganizationIdPATCH = function(userId,organizationId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Create a new External Profile for given user id. **Security** - This endpoint is accessible by all authenticated users. - * - * userId String The user id - * body ExternalProfileRequestBody - * returns ExternalProfile - **/ -exports.usersUserIdExternalProfilesPOST = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/OrganizationsService.js b/service/OrganizationsService.js deleted file mode 100644 index 6a26c5d..0000000 --- a/service/OrganizationsService.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - - -/** - * Search organizations in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by organization name (optional) - * returns List - **/ -exports.organizationsGET = function(name) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on organizations in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by organization name (optional) - * no response value expected for this operation - **/ -exports.organizationsHEAD = function(name) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Remove an existing organization with given id. **Security** - Note that this endpoint is only available for admin users. - * - * organizationId String The organization id - * no response value expected for this operation - **/ -exports.organizationsOrganizationIdDELETE = function(organizationId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get organization with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * organizationId String The organization id - * returns Organization - **/ -exports.organizationsOrganizationIdGET = function(organizationId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get organization with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * organizationId String The organization id - * no response value expected for this operation - **/ -exports.organizationsOrganizationIdHEAD = function(organizationId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing organization with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * organizationId String The organization id - * body NameRequestBody - * returns Organization - **/ -exports.organizationsOrganizationIdPATCH = function(organizationId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Create a new Organization. **Security** - This endpoint is accessible by all authenticated users. - * - * body NameRequestBody - * returns Organization - **/ -exports.organizationsPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/RolesService.js b/service/RolesService.js deleted file mode 100644 index bf9ad3e..0000000 --- a/service/RolesService.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - - -/** - * Search Roles in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by role name (optional) - * returns List - **/ -exports.rolesGET = function(name) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on Roles in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by role name (optional) - * no response value expected for this operation - **/ -exports.rolesHEAD = function(name) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Role. **Security** - This endpoint is accessible by all authenticated users. - * - * body NameRequestBody - * returns Role - **/ -exports.rolesPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing role with given id. **Security** - Note that this endpoint is only available for admin users. - * - * roleId String The role id - * no response value expected for this operation - **/ -exports.rolesRoleIdDELETE = function(roleId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get role with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * roleId String The role id - * returns Role - **/ -exports.rolesRoleIdGET = function(roleId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get role with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * roleId String The role id - * no response value expected for this operation - **/ -exports.rolesRoleIdHEAD = function(roleId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing role with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * roleId String The role id - * body NameRequestBody - * returns Role - **/ -exports.rolesRoleIdPATCH = function(roleId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/SkillsProviderService.js b/service/SkillsProviderService.js deleted file mode 100644 index e420a86..0000000 --- a/service/SkillsProviderService.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - - -/** - * Search Skills Provider in the application. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by provider name (optional) - * returns List - **/ -exports.skillsProvidersGET = function(name) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on skills providers in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * name String Filter by provider name (optional) - * no response value expected for this operation - **/ -exports.skillsProvidersHEAD = function(name) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Skills Provider. **Security** - This endpoint is accessible by all authenticated users. - * - * body NameRequestBody - * returns SkillsProvider - **/ -exports.skillsProvidersPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing skills provider with given id. **Security** - Note that this endpoint is only available for admin users. - * - * providerId String The provider id - * no response value expected for this operation - **/ -exports.skillsProvidersProviderIdDELETE = function(providerId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get skills provider with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * providerId String The provider id - * returns SkillsProvider - **/ -exports.skillsProvidersProviderIdGET = function(providerId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get skills provider with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * providerId String The provider id - * no response value expected for this operation - **/ -exports.skillsProvidersProviderIdHEAD = function(providerId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing skills provider with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * providerId String The provider id - * body NameRequestBody - * returns SkillsProvider - **/ -exports.skillsProvidersProviderIdPATCH = function(providerId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/SkillsService.js b/service/SkillsService.js deleted file mode 100644 index d5184e7..0000000 --- a/service/SkillsService.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; - - -/** - * Get list of skills in the application. If no results, then empty array is returned. Multiple filters are supported. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * returns List - **/ -exports.skillsGET = function() { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for get operation on Skills in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * no response value expected for this operation - **/ -exports.skillsHEAD = function() { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new Skill. **Security** - This endpoint is accessible by all authenticated users. - * - * body SkillRequestBody - * returns Skill - **/ -exports.skillsPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing skill with given id. **Security** - Note that this endpoint is only available for admin users. - * - * skillId String The skill id - * no response value expected for this operation - **/ -exports.skillsSkillIdDELETE = function(skillId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get Skill by given skill id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * skillId String The skill id - * returns Skill - **/ -exports.skillsSkillIdGET = function(skillId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for get operation on Skill by its Id in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * skillId String The user id - * no response value expected for this operation - **/ -exports.skillsSkillIdHEAD = function(skillId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing skill with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * skillId String The skill id - * body SkillUpdateRequestBody - * returns Skill - **/ -exports.skillsSkillIdPATCH = function(skillId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/UserAttributesService.js b/service/UserAttributesService.js deleted file mode 100644 index bba4518..0000000 --- a/service/UserAttributesService.js +++ /dev/null @@ -1,129 +0,0 @@ -'use strict'; - - -/** - * Remove an existing User Attribute with given ids. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * attributeId String The attribute id - * no response value expected for this operation - **/ -exports.usersUserIdAttributesAttributeIdDELETE = function(userId,attributeId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get User Attributes with given user and attribute id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * attributeId String The attribute id - * returns UserAttribute - **/ -exports.usersUserIdAttributesAttributeIdGET = function(userId,attributeId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get User Attributes with given ids, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * attributeId String The attribute id - * no response value expected for this operation - **/ -exports.usersUserIdAttributesAttributeIdHEAD = function(userId,attributeId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing user attribute with given ids. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * userId String The user id - * attributeId String The attribute id - * body UserAttributeUpdateRequestBody - * returns UserAttribute - **/ -exports.usersUserIdAttributesAttributeIdPATCH = function(userId,attributeId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get attributes for the given user. Optionally, 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. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * attributeName String Filter by the attribute name (optional) - * attributeGroupName String Filter by the attribute group name (optional) - * attributeGroupId String Filter by the attribute group id (optional) - * returns List - **/ -exports.usersUserIdAttributesGET = function(userId,attributeName,attributeGroupName,attributeGroupId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on users attributes in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * attributeName String Filter by the attribute name (optional) - * attributeGroupName String Filter by the attribute group name (optional) - * attributeGroupId String Filter by the attribute group id (optional) - * no response value expected for this operation - **/ -exports.usersUserIdAttributesHEAD = function(userId,attributeName,attributeGroupName,attributeGroupId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new User Attribute. **Security** - This endpoint is accessible by all authenticated users. - * - * userId String The user id - * body UserAttributeRequestBody - * returns UserAttribute - **/ -exports.usersUserIdAttributesPOST = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/UserRolesService.js b/service/UserRolesService.js deleted file mode 100644 index da53006..0000000 --- a/service/UserRolesService.js +++ /dev/null @@ -1,102 +0,0 @@ -'use strict'; - - -/** - * Get User Roles that belong to given user id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * returns List - **/ -exports.usersUserIdRolesGET = function(userId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get User Roles that belong to given user id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * no response value expected for this operation - **/ -exports.usersUserIdRolesHEAD = function(userId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new User Role. **Security** - This endpoint is accessible by all authenticated users. - * - * userId String The user id - * body UserRoleRequestBody - * returns UserRole - **/ -exports.usersUserIdRolesPOST = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing user role with given user and role id. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * roleId String The role id - * no response value expected for this operation - **/ -exports.usersUserIdRolesRoleIdDELETE = function(userId,roleId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get role by its id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * roleId String The role id - * returns List - **/ -exports.usersUserIdRolesRoleIdGET = function(userId,roleId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on User Roles in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * roleId String The role id - * no response value expected for this operation - **/ -exports.usersUserIdRolesRoleIdHEAD = function(userId,roleId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - diff --git a/service/UsersService.js b/service/UsersService.js deleted file mode 100644 index 0fc7473..0000000 --- a/service/UsersService.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict'; - - -/** - * **Point to note** - For non-admin users, this endpoint will only return entities that the user has created. - * - * handle String Filter by user handle (optional) - * groupId String Filter by user group Id (optional) - * roleId String Filter by user roleId (optional) - * returns List - **/ -exports.usersGET = function(handle,groupId,roleId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on users in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * handle String Filter by user handle (optional) - * groupId String Filter by user group Id (optional) - * roleId String Filter by user roleId (optional) - * no response value expected for this operation - **/ -exports.usersHEAD = function(handle,groupId,roleId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new User. **Security** - This endpoint is accessible by all authenticated users. - * - * body UserRequestBody - * returns User - **/ -exports.usersPOST = function(body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing User with given id. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * no response value expected for this operation - **/ -exports.usersUserIdDELETE = function(userId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get User with given id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * returns User - **/ -exports.usersUserIdGET = function(userId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get User with given id, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * no response value expected for this operation - **/ -exports.usersUserIdHEAD = function(userId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing User with given id. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * userId String The user id - * body UserUpdateRequestBody - * returns User - **/ -exports.usersUserIdPATCH = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/service/UsersSkillsService.js b/service/UsersSkillsService.js deleted file mode 100644 index 55e1152..0000000 --- a/service/UsersSkillsService.js +++ /dev/null @@ -1,125 +0,0 @@ -'use strict'; - - -/** - * Filter skills by its name given an user id. If no results, then empty array is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * skillName String Filter by skill name (through skill id) (optional) - * returns List - **/ -exports.usersUserIdSkillsGET = function(userId,skillName) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = [ "", "" ]; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Retrieve header information for a search operation on users skills in the application. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * skillName String Filter by skill name (through skill id) (optional) - * no response value expected for this operation - **/ -exports.usersUserIdSkillsHEAD = function(userId,skillName) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Create a new User Skill. **Security** - This endpoint is accessible by all authenticated users. - * - * userId String The user id - * body UserSkillRequestBody - * returns UserSkill - **/ -exports.usersUserIdSkillsPOST = function(userId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Remove an existing User Skill with given ids. **Security** - Note that this endpoint is only available for admin users. - * - * userId String The user id - * skillId String The skill id - * no response value expected for this operation - **/ -exports.usersUserIdSkillsSkillIdDELETE = function(userId,skillId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Get User Skills with given user and skill id. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * skillId String The skill id - * returns UserSkill - **/ -exports.usersUserIdSkillsSkillIdGET = function(userId,skillId) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - - -/** - * Get User Skills with given ids, but only header information is returned. **Security** - Note that for non-admin users, this endpoint will only return entities that the user has created. - * - * userId String The user id - * skillId String The skill id - * no response value expected for this operation - **/ -exports.usersUserIdSkillsSkillIdHEAD = function(userId,skillId) { - return new Promise(function(resolve, reject) { - resolve(); - }); -} - - -/** - * Update an existing skill with given ids. **Security** - Note that for non-admin users, this endpoint will only allow updates on entities that the calling user has created. - * - * userId String The user id - * skillId String The skill id - * body UserSkillUpdateRequestBody - * returns UserSkill - **/ -exports.usersUserIdSkillsSkillIdPATCH = function(userId,skillId,body) { - return new Promise(function(resolve, reject) { - var examples = {}; - examples['application/json'] = ""; - if (Object.keys(examples).length > 0) { - resolve(examples[Object.keys(examples)[0]]); - } else { - resolve(); - } - }); -} - diff --git a/src/bootstrap.js b/src/bootstrap.js new file mode 100755 index 0000000..ef7e2e9 --- /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 decorates 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..e04eebb --- /dev/null +++ b/src/common/controller-helper.js @@ -0,0 +1,134 @@ +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)) + } + + /** + * search entities by request query + * @param req the http request + * @param res the http response + */ + async function search (req, res) { + const { items, meta } = await service.search(req.query, req.auth) + injectSearchMeta(res, meta) + res.json(items) + } + + /** + * 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) { + res.json(await service.get(req.params.id, req.auth, _.omit(req.params, 'id'))) + } + + /** + * search entities by request query + * @param req the http request + * @param res the http response + */ + async function search (req, res) { + const { items, meta } = await service.search(_.extend(req.query, req.params), req.auth) + injectSearchMeta(res, meta) + res.json(items) + } + + /** + * 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..d1fd740 --- /dev/null +++ b/src/common/errors.js @@ -0,0 +1,25 @@ +/** + * 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.') +} diff --git a/src/common/helper.js b/src/common/helper.js new file mode 100644 index 0000000..9caf0c8 --- /dev/null +++ b/src/common/helper.js @@ -0,0 +1,186 @@ +const { + Decimal, + IonTypes, + Timestamp +} = require('ion-js') + +const errors = require('./errors') +const appConst = require('../consts') +const _ = require('lodash') +const { getServiceMethods } = require('./service-helper') +const { getControllerMethods, getSubControllerMethods } = require('./controller-helper') + +/** + * 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 +} + +/** + * no paging in database, so only inject X-Total + * @param res the response + * @param meta the metadata + */ +function injectSearchMeta (res, meta) { + res.header({ 'X-Total': meta.total }) +} + +/** + * 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.UserRoles.admin]) && + recordObj.createdBy !== getAuthUser(auth)) { + throw errors.newPermissionError('You are not allowed to perform this action') + } +} + +module.exports = { + writeValueAsIon, + readerToJson, + getAuthUser, + permissionCheck, + checkIfExists, + injectSearchMeta, + getControllerMethods, + getSubControllerMethods, + getServiceMethods +} 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..6486af9 --- /dev/null +++ b/src/common/service-helper.js @@ -0,0 +1,204 @@ +const joi = require('@hapi/joi') +const appConst = require('../consts') +const _ = require('lodash') +const errors = require('./errors') + +/** + * 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 +} + +/** + * 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 { 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) + 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) + 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 query params + * @return {Promise} the db device + */ + async function get (id, auth, params) { + let recordObj + if (_.isNil(params) || _.isEmpty(params)) { + recordObj = await models.DBHelper.get(Model, id) + } else { + const items = await models.DBHelper.find(Model, buildQueryByParams(params)) + recordObj = items[0] + if (!recordObj) { + throw errors.newEntityNotFoundError(`cannot find ${Model.tableName} where ${_.map(params, (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) { + 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.UserRoles.admin])) { + dbQueries.push(`${Model.tableName}.createdBy = '${getAuthUser(auth)}'`) + } + const items = await models.DBHelper.find(Model, dbQueries) + return { items, meta: { 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) { + await get(id, auth, params) // check exist + await models.DBHelper.delete(Model, id, buildQueryByParams(params)) + } + + 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..654824f --- /dev/null +++ b/src/consts.js @@ -0,0 +1,26 @@ +/** + * 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' +} +/** + * all authenticated users. + * @type {(string)[]} + */ +const AllAuthenticatedUsers = [UserRoles.admin, UserRoles.administrator, UserRoles.topcoderUser, UserRoles.copilot] + +/** + * 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..89a2da5 --- /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, { + region: config.AWS_REGION, + secretAccessKey: config.AWS_SECRET, + accessKeyId: config.AWS_KEY +}) + +/** + * 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..f90fe2b --- /dev/null +++ b/src/models/ExternalProfile.js @@ -0,0 +1,17 @@ +const { RecordObject } = require('./BaseObject') + +/** + * ExternalProfile model + */ +class ExternalProfile extends RecordObject { + constructor () { + super() + this.userId = null + this.organizationId = null + this.uri = 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/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..f63a7d5 --- /dev/null +++ b/src/models/User.js @@ -0,0 +1,21 @@ +const { RecordObject } = require('./BaseObject') + +/** + * User model + */ +class User extends RecordObject { + constructor () { + super() + this.handle = 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..a7b4c47 --- /dev/null +++ b/src/models/index.js @@ -0,0 +1,58 @@ +/** + * 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 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, + 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) +} 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..e868e52 --- /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().required(), + certifierId: joi.string().required(), + certifiedDate: joi.date().format('iso').required() + }, + { // 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..8d3cc6d --- /dev/null +++ b/src/modules/externalProfile/service.js @@ -0,0 +1,39 @@ +/** + * 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(), + uri: joi.string().required() + }, + { // patch request body joi schema + userId: joi.string().required(), + organizationId: joi.string().required(), + uri: joi.string() + }, + { // search request query joi schema + userId: joi.string().required(), + organizationName: joi.string() + }, + 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}%'`) + } + return dbQueries + }, + [['userId', 'organizationId']] // unique fields +) + +module.exports = { + ...methods +} 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/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/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..c63f278 --- /dev/null +++ b/src/modules/skill/service.js @@ -0,0 +1,27 @@ +/** + * 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().required(), + externalId: joi.string().required() + }, + { + skillProviderId: joi.string(), + name: joi.string(), + uri: joi.string(), + externalId: joi.string() + }, + {}, + async () => []) + +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..a737b3c --- /dev/null +++ b/src/modules/user/service.js @@ -0,0 +1,30 @@ +/** + * 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() }, + { handle: 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..3b369ec --- /dev/null +++ b/src/modules/usersRole/route.js @@ -0,0 +1,54 @@ +/** + * 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'] + }, + patch: { + method: Controller.patch, + auth: 'jwt', + access: consts.AllAuthenticatedUsers, + scopes: ['update: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..b6fd2bd --- /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, + 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..d8ceff7 --- /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().required(), + certifierId: joi.string().required(), + certifiedDate: joi.date().format('iso').required() + }, + { + 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, '.')) diff --git a/utils/writer.js b/utils/writer.js deleted file mode 100644 index d79f6e1..0000000 --- a/utils/writer.js +++ /dev/null @@ -1,43 +0,0 @@ -var ResponsePayload = function(code, payload) { - this.code = code; - this.payload = payload; -} - -exports.respondWithCode = function(code, payload) { - return new ResponsePayload(code, payload); -} - -var writeJson = exports.writeJson = function(response, arg1, arg2) { - var code; - var payload; - - if(arg1 && arg1 instanceof ResponsePayload) { - writeJson(response, arg1.payload, arg1.code); - return; - } - - if(arg2 && Number.isInteger(arg2)) { - code = arg2; - } - else { - if(arg1 && Number.isInteger(arg1)) { - code = arg1; - } - } - if(code && arg1) { - payload = arg1; - } - else if(arg1) { - payload = arg1; - } - - if(!code) { - // if no response code given, we default to 200 - code = 200; - } - if(typeof payload === 'object') { - payload = JSON.stringify(payload, null, 2); - } - response.writeHead(code, {'Content-Type': 'application/json'}); - response.end(payload); -} From 4158df18be0e08fc62940a77e145720fdcb39440 Mon Sep 17 00:00:00 2001 From: CWD Date: Thu, 21 May 2020 16:11:34 -0400 Subject: [PATCH 05/93] making version dynamic from env vars --- config/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/default.js b/config/default.js index 356a4f5..7aa86e9 100755 --- a/config/default.js +++ b/config/default.js @@ -8,7 +8,7 @@ module.exports = { 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"]', - API_VERSION: 'api/1.0', + API_VERSION: process.env.API_VERSION || 'api/1.0', AWS_KEY: process.env.AWS_KEY, AWS_SECRET: process.env.AWS_SECRET, From 0d9ad99e3d014a1609df230327637a4b1aa31b6d Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 28 May 2020 11:35:48 +0530 Subject: [PATCH 06/93] Add /health endpoint for health check --- src/common/errors.js | 3 ++- src/modules/health/controller.js | 11 +++++++++++ src/modules/health/route.js | 12 ++++++++++++ src/modules/health/service.js | 17 +++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/modules/health/controller.js create mode 100644 src/modules/health/route.js create mode 100644 src/modules/health/service.js diff --git a/src/common/errors.js b/src/common/errors.js index d1fd740..546ccee 100644 --- a/src/common/errors.js +++ b/src/common/errors.js @@ -21,5 +21,6 @@ module.exports = { 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.') + newConflictError: msg => new AppError(409, msg || 'The entity does not exist.'), + serviceUnavailableError: msg => new AppError(503, msg || 'One or more services are not available') } 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..1189977 --- /dev/null +++ b/src/modules/health/route.js @@ -0,0 +1,12 @@ +/** + * the health routes + */ + +const Controller = require('./controller') +module.exports = { + '/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 } From 6c2e4dfb873999694600bc7291ce15e102d9004b Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 31 May 2020 22:27:52 +0530 Subject: [PATCH 07/93] Integrate elasticsearch --- README.md | 67 +- VERIFICATION.md | 102 ++ config/default.js | 83 +- docker/Dockerfile | 12 + docker/docker-compose.yml | 11 + docker/sample.api.env | 9 + docs/UBahn_API.postman_collection.json | 1505 ++++++++---------------- docs/swagger.yaml | 46 +- package-lock.json | 210 +++- package.json | 6 +- src/common/controller-helper.js | 16 +- src/common/errors.js | 3 +- src/common/es-client.js | 43 + src/common/es-helper.js | 731 ++++++++++++ src/common/group-api.js | 29 + src/common/helper.js | 82 +- src/common/service-helper.js | 10 +- 17 files changed, 1895 insertions(+), 1070 deletions(-) create mode 100644 VERIFICATION.md create mode 100644 docker/Dockerfile create mode 100644 docker/docker-compose.yml create mode 100644 docker/sample.api.env create mode 100644 src/common/es-client.js create mode 100644 src/common/es-helper.js create mode 100644 src/common/group-api.js diff --git a/README.md b/README.md index 32fef12..700b0ec 100755 --- a/README.md +++ b/README.md @@ -5,9 +5,46 @@ - 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_KEY: The AWS access key +- AWS_SECRET: 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.AWS_REGION: The Amazon region to use when using AWS Elasticsearch service +- 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 .. @@ -15,31 +52,29 @@ 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` -## Docker +## 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 -- Run `docker build -t tc/ubahn_api .` to build image -- Then run `docker run tc/ubahn_api -d` to startup image +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 -## Configuration - -| key | system Environment name | description | -| ------------- | ----------------------- | -------------------------- | -| PORT | PORT | the server port | -| AUTH_SECRET | AUTH_SECRET | the jwt client secret | -| VALID_ISSUERS | VALID_ISSUERS | jwt token issuers | -| API_VERSION | | the api prefix version | -| AWS_KEY | AWS_KEY | the aws Access key | -| AWS_SECRET | AWS_SECRET | the aws Access secret | -| AWS_REGION | AWS_REGION | the aws service region | -| DATABASE | DATABASE | the aws QLDB database name | - ## Test token you can use below token to test role and permissions 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/config/default.js b/config/default.js index 356a4f5..3ddae92 100755 --- a/config/default.js +++ b/config/default.js @@ -5,13 +5,92 @@ 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"]', - API_VERSION: 'api/1.0', + + PAGE_SIZE: process.env.PAGE_SIZE || 20, + API_VERSION: process.env.API_VERSION || 'api/1.0', AWS_KEY: process.env.AWS_KEY, AWS_SECRET: process.env.AWS_SECRET, AWS_REGION: process.env.AWS_REGION || 'us-east-1', - DATABASE: process.env.DATABASE || 'ubahn-db' + 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', + + // ElasticSearch + ES: { + HOST: process.env.ES_HOST || 'localhost:9200', + AWS_REGION: process.env.AWS_REGION || 'us-east-1', // AWS Region to be used if we use AWS ES + API_VERSION: process.env.ES_API_VERSION || '6.8', + // 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' + } + } + } } 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 index b445a55..1204bed 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -1,8 +1,8 @@ { "info": { - "_postman_id": "67870cba-bdd9-4825-9f61-b747ae6d4757", + "_postman_id": "5c1cb83a-4ae3-4e5b-b89d-d59005e44b70", "name": "UBahn_API", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" }, "item": [ { @@ -40,22 +40,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"handle\":\"handle_01\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"handle\":\"handle_01\"\n}" }, - "url": { - "raw": "{{HOST}}/users", - "host": [ - "{{HOST}}" - ], - "path": [ - "users" - ] - } + "url": "{{HOST}}/users" }, "response": [] }, @@ -70,14 +57,24 @@ "value": "Bearer {{token}}" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users/{{userId}}", + "raw": "{{HOST}}/users/{{userId}}?enrich=true", "host": [ "{{HOST}}" ], "path": [ "users", "{{userId}}" + ], + "query": [ + { + "key": "enrich", + "value": "true" + } ] } }, @@ -102,23 +99,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"handle\":\"handle_05\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"handle\":\"handle_05\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}" }, "response": [] }, @@ -139,16 +122,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}" }, "response": [] }, @@ -163,8 +141,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users?handle=h&roleId=8607ddb3-abf6-4512-a618-c60d4771174b", + "raw": "{{HOST}}/users?enrich=true&role.name=role-2", "host": [ "{{HOST}}" ], @@ -173,12 +155,42 @@ ], "query": [ { - "key": "handle", - "value": "h" + "key": "enrich", + "value": "true" }, { "key": "roleId", - "value": "8607ddb3-abf6-4512-a618-c60d4771174b" + "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" } ] } @@ -196,15 +208,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/users", - "host": [ - "{{HOST}}" - ], - "path": [ - "users" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users" }, "response": [] }, @@ -225,21 +233,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "roles", @@ -276,22 +278,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"Admin\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"Admin\"\n}" }, - "url": { - "raw": "{{HOST}}/roles", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles" - ] - } + "url": "{{HOST}}/roles" }, "response": [] }, @@ -306,16 +295,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/roles/{{roleId}}" }, "response": [] }, @@ -338,23 +322,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"Admin02\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"Admin02\"\n}" }, - "url": { - "raw": "{{HOST}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles", - "{{roleId}}" - ] - } + "url": "{{HOST}}/roles/{{roleId}}" }, "response": [] }, @@ -375,16 +345,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/roles/{{roleId}}" }, "response": [] }, @@ -399,6 +364,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/roles?name=m", "host": [ @@ -428,15 +397,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/roles", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/roles" }, "response": [] }, @@ -457,21 +422,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/roles/{{roleId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "usersRoles", @@ -507,24 +466,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"roleId\":\"{{roleId}}\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"roleId\":\"{{roleId}}\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles" - ] - } + "url": "{{HOST}}/users/{{userId}}/roles" }, "response": [] }, @@ -539,18 +483,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" }, "response": [] }, @@ -573,25 +510,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"roleId\":\"8607ddb3-abf6-4512-a618-c60d4771174b\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"roleId\":\"8607ddb3-abf6-4512-a618-c60d4771174b\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles", - "{{roleId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" }, "response": [] }, @@ -612,18 +533,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" }, "response": [] }, @@ -638,17 +552,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/roles" }, "response": [] }, @@ -663,17 +571,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/roles" }, "response": [] }, @@ -694,23 +596,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "roles", - "{{roleId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "organizations", @@ -747,22 +641,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"organization_01\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"organization_01\"\n}" }, - "url": { - "raw": "{{HOST}}/organizations", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations" - ] - } + "url": "{{HOST}}/organizations" }, "response": [] }, @@ -777,16 +658,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/organizations/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/organizations/603d4264-cdb0-47f1-914e-f053abc60422" }, "response": [] }, @@ -809,23 +685,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"organization_update\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"organization_update\"\n}" }, - "url": { - "raw": "{{HOST}}/organizations/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations", - "{{organizationId}}" - ] - } + "url": "{{HOST}}/organizations/{{organizationId}}" }, "response": [] }, @@ -846,16 +708,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/organizations/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/organizations/{{organizationId}}" }, "response": [] }, @@ -870,6 +727,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/organizations?name=o", "host": [ @@ -899,15 +760,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/organizations", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/organizations" }, "response": [] }, @@ -928,21 +785,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/organizations/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "organizations", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/organizations/{{organizationId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "skillsProviders", @@ -979,22 +830,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skillsProviders_01\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"skillsProviders_01\"\n}" }, - "url": { - "raw": "{{HOST}}/skillsProviders", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders" - ] - } + "url": "{{HOST}}/skillsProviders" }, "response": [] }, @@ -1009,16 +847,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders", - "{{skillsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" }, "response": [] }, @@ -1041,23 +874,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skillsProviders_update\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"skillsProviders_update\"\n}" }, - "url": { - "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders", - "{{skillsProviderId}}" - ] - } + "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" }, "response": [] }, @@ -1078,16 +897,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders", - "{{skillsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" }, "response": [] }, @@ -1102,6 +916,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/skillsProviders?name=ski", "host": [ @@ -1131,15 +949,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/skillsProviders", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skillsProviders" }, "response": [] }, @@ -1160,21 +974,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skillsProviders", - "{{skillsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "skills", @@ -1211,22 +1019,9 @@ ], "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": { - "language": "json" - } - } + "raw": "{\n\t\"skillProviderId\":\"{{skillsProviderId}}\",\n\t\"name\":\"jump\",\n\t\"uri\":\"http://www.google.com\",\n\t\"externalId\":\"externalId\"\n}" }, - "url": { - "raw": "{{HOST}}/skills", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills" - ] - } + "url": "{{HOST}}/skills" }, "response": [] }, @@ -1241,16 +1036,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skills/{{skillId}}" }, "response": [] }, @@ -1273,23 +1063,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skill_name_update\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"skill_name_update\"\n}" }, - "url": { - "raw": "{{HOST}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills", - "{{skillId}}" - ] - } + "url": "{{HOST}}/skills/{{skillId}}" }, "response": [] }, @@ -1310,16 +1086,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skills/{{skillId}}" }, "response": [] }, @@ -1334,6 +1105,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/skills", "host": [ @@ -1341,6 +1116,13 @@ ], "path": [ "skills" + ], + "query": [ + { + "key": "perPage", + "value": "2", + "disabled": true + } ] } }, @@ -1357,15 +1139,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/skills", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skills" }, "response": [] }, @@ -1386,21 +1164,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/skills/{{skillId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "usersSkills", @@ -1431,29 +1203,14 @@ "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": { - "language": "json" - } - } - }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills" - ] - } + "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}" + }, + "url": "{{HOST}}/users/{{userId}}/skills" }, "response": [] }, @@ -1468,18 +1225,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" }, "response": [] }, @@ -1502,25 +1252,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"metricValue\":\"4.5L\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"metricValue\":\"4.5L\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills", - "{{skillId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" }, "response": [] }, @@ -1541,18 +1275,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" }, "response": [] }, @@ -1567,8 +1294,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users/{{userId}}/skills", + "raw": "{{HOST}}/users/{{userId}}/skills?skillName=skill-2", "host": [ "{{HOST}}" ], @@ -1580,8 +1311,7 @@ "query": [ { "key": "skillName", - "value": "ju", - "disabled": true + "value": "skill-2" } ] } @@ -1599,17 +1329,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/skills" }, "response": [] }, @@ -1630,23 +1354,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "skills", - "{{skillId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "externalProfiles", @@ -1682,24 +1398,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"uri\":\"http://uri.com/uri\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"uri\":\"http://uri.com/uri\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles" - ] - } + "url": "{{HOST}}/users/{{userId}}/externalProfiles" }, "response": [] }, @@ -1714,18 +1415,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" }, "response": [] }, @@ -1748,25 +1442,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"uri\":\"http://www.new.com/new-uri\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"uri\":\"http://www.new.com/new-uri\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles", - "{{organizationId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" }, "response": [] }, @@ -1787,18 +1465,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" }, "response": [] }, @@ -1813,8 +1484,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles?organizationName=o", + "raw": "{{HOST}}/users/{{userId}}/externalProfiles?organizationName=organization", "host": [ "{{HOST}}" ], @@ -1826,7 +1501,7 @@ "query": [ { "key": "organizationName", - "value": "o" + "value": "organization" } ] } @@ -1844,17 +1519,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles" }, "response": [] }, @@ -1875,23 +1544,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "achievementsProviders", @@ -1928,22 +1589,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"achievementsProviders_02\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"achievementsProviders_02\"\n}" }, - "url": { - "raw": "{{HOST}}/achievementsProviders", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders" - ] - } + "url": "{{HOST}}/achievementsProviders" }, "response": [] }, @@ -1958,16 +1606,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders", - "{{achievementsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" }, "response": [] }, @@ -1990,23 +1633,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"achievementsProviders_update\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"achievementsProviders_update\"\n}" }, - "url": { - "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders", - "{{achievementsProviderId}}" - ] - } + "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" }, "response": [] }, @@ -2027,16 +1656,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders", - "{{achievementsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" }, "response": [] }, @@ -2051,6 +1675,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/achievementsProviders?name=a", "host": [ @@ -2080,15 +1708,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/achievementsProviders", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/achievementsProviders" }, "response": [] }, @@ -2109,21 +1733,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "achievementsProviders", - "{{achievementsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "achievements", @@ -2159,24 +1777,9 @@ ], "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": { - "language": "json" - } - } + "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}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/achievements", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "achievements" - ] - } + "url": "{{HOST}}/users/{{userId}}/achievements" }, "response": [] }, @@ -2191,18 +1794,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "achievements", - "{{achievementsProviderId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/achievements/7b4f98b1-5831-45fe-a71f-8454d11eb8e8" }, "response": [] }, @@ -2225,25 +1821,9 @@ ], "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": { - "language": "json" - } - } + "raw": "{\n\t \"name\": \"string\",\n\t \"uri\": \"string\",\n\t \"certifierId\": \"string\",\n\t \"certifiedDate\": \"2020-05-13T06:33:54.708Z\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "achievements", - "{{achievementsProviderId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}" }, "response": [] }, @@ -2261,21 +1841,14 @@ "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}}" - ] - } + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}" }, "response": [] }, @@ -2290,8 +1863,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users/{{userId}}/achievements", + "raw": "{{HOST}}/users/{{userId}}/achievements?", "host": [ "{{HOST}}" ], @@ -2322,17 +1899,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles" }, "response": [] }, @@ -2353,23 +1924,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "externalProfiles", - "{{organizationId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "attributeGroups", @@ -2406,22 +1969,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"name\":\"attributeGroup_01\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"name\":\"attributeGroup_01\"\n}" }, - "url": { - "raw": "{{HOST}}/attributeGroups", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups" - ] - } + "url": "{{HOST}}/attributeGroups" }, "response": [] }, @@ -2436,16 +1986,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups", - "{{attributeGroupId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" }, "response": [] }, @@ -2468,23 +2013,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"group 03\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"group 03\"\n}" }, - "url": { - "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups", - "{{attributeGroupId}}" - ] - } + "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" }, "response": [] }, @@ -2505,16 +2036,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups", - "{{attributeGroupId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" }, "response": [] }, @@ -2529,8 +2055,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/attributeGroups?name=roup", + "raw": "{{HOST}}/attributeGroups?", "host": [ "{{HOST}}" ], @@ -2540,11 +2070,12 @@ "query": [ { "key": "name", - "value": "roup" + "value": "attributegroup", + "disabled": true }, { "key": "organizationId", - "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "value": "603d4264-cdb0-47f1-914e-f053abc60422", "disabled": true } ] @@ -2563,15 +2094,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/attributeGroups", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributeGroups" }, "response": [] }, @@ -2592,21 +2119,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributeGroups", - "{{attributeGroupId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "attributes", @@ -2643,22 +2164,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"attributeGroupId\":\"{{attributeGroupId}}\",\n\t\"name\":\"attribute_02\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"attributeGroupId\":\"{{attributeGroupId}}\",\n\t\"name\":\"attribute_02\"\n}" }, - "url": { - "raw": "{{HOST}}/attributes", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes" - ] - } + "url": "{{HOST}}/attributes" }, "response": [] }, @@ -2673,16 +2181,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributes/{{attributeId}}" }, "response": [] }, @@ -2705,23 +2208,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"attr-04\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"name\":\"attr-04\"\n}" }, - "url": { - "raw": "{{HOST}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes", - "{{attributeId}}" - ] - } + "url": "{{HOST}}/attributes/{{attributeId}}" }, "response": [] }, @@ -2742,16 +2231,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributes/{{attributeId}}" }, "response": [] }, @@ -2766,6 +2250,10 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { "raw": "{{HOST}}/attributes?name=attr", "host": [ @@ -2800,15 +2288,11 @@ "type": "text" } ], - "url": { - "raw": "{{HOST}}/attributes", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributes" }, "response": [] }, @@ -2829,21 +2313,15 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/attributes/{{attributeId}}" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] }, { "name": "userAttributes", @@ -2879,24 +2357,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"attributeId\":\"{{attributeId}}\",\n\t\"value\":\"1.23\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"attributeId\":\"{{attributeId}}\",\n\t\"value\":\"1.23\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes" - ] - } + "url": "{{HOST}}/users/{{userId}}/attributes" }, "response": [] }, @@ -2911,18 +2374,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/attributes/b746ef65-336d-4846-b02a-f25f6cff72c9" }, "response": [] }, @@ -2945,25 +2401,9 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"value\":\"2.56\"\n}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{\n\t\"value\":\"2.56\"\n}" }, - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes", - "{{attributeId}}" - ] - } + "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" }, "response": [] }, @@ -2984,18 +2424,11 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" }, "response": [] }, @@ -3010,8 +2443,12 @@ "type": "text" } ], + "body": { + "mode": "raw", + "raw": "" + }, "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes?attributeName=attr&attributeGroupName=roup", + "raw": "{{HOST}}/users/{{userId}}/attributes?attributeName=attribute&attributeGroupName=attributegroup&attributeGroupId=720c34f9-0fd4-46fd-9293-4a8cfdcd3e96", "host": [ "{{HOST}}" ], @@ -3023,16 +2460,15 @@ "query": [ { "key": "attributeName", - "value": "attr" + "value": "attribute" }, { "key": "attributeGroupName", - "value": "roup" + "value": "attributegroup" }, { "key": "attributeGroupId", - "value": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", - "disabled": true + "value": "720c34f9-0fd4-46fd-9293-4a8cfdcd3e96" } ] } @@ -3050,17 +2486,11 @@ "value": "Bearer {{token}}" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/attributes" }, "response": [] }, @@ -3081,24 +2511,39 @@ "value": "application/json" } ], - "url": { - "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", - "host": [ - "{{HOST}}" - ], - "path": [ - "users", - "{{userId}}", - "attributes", - "{{attributeId}}" - ] - } + "body": { + "mode": "raw", + "raw": "" + }, + "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" + }, + "response": [] + } + ] + }, + { + "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" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": "https://api.topcoder-dev.com/v5/groups" }, "response": [] } - ], - "protocolProfileBehavior": {} + ] } - ], - "protocolProfileBehavior": {} -} + ] +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index fb774fd..c6b0d9b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -50,6 +50,8 @@ paths: \ 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" @@ -294,6 +296,8 @@ paths: \ 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" @@ -556,7 +560,9 @@ paths: \ that for non-admin users, this endpoint will only return entities that\n\ the user has created.\n" operationId: "skillsGET" - parameters: [] + parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' responses: "200": description: "OK - the request was successful" @@ -768,6 +774,8 @@ paths: \ 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" @@ -988,6 +996,8 @@ paths: \ 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" @@ -1208,6 +1218,8 @@ paths: the user has created. \n" operationId: "usersUserIdRolesGET" parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' - name: "userId" in: "path" description: "The user id" @@ -1417,6 +1429,8 @@ paths: the user has created. \n" operationId: "usersUserIdExternalProfilesGET" parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' - name: "userId" in: "path" description: "The user id" @@ -1680,6 +1694,8 @@ paths: \ has created. \n" operationId: "usersUserIdAchievementsGET" parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' - name: "userId" in: "path" description: "The user id" @@ -1951,6 +1967,8 @@ paths: \ 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" @@ -2171,6 +2189,8 @@ paths: \ 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" @@ -2393,6 +2413,8 @@ paths: \ that\nthe user has created.\n" operationId: "usersUserIdAttributesGET" parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' - name: "userId" in: "path" description: "The user id" @@ -2677,6 +2699,8 @@ paths: \ 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" @@ -2900,6 +2924,8 @@ paths: \ that\nthe user has created.\n" operationId: "attributeGroupsGET" parameters: + - $ref: '#/parameters/page' + - $ref: '#/parameters/perPage' - name: "name" in: "query" description: "Filter by group name" @@ -3129,6 +3155,24 @@ securityDefinitions: 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" diff --git a/package-lock.json b/package-lock.json index 0c63726..573f090 100644 --- a/package-lock.json +++ b/package-lock.json @@ -188,6 +188,14 @@ "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", @@ -545,6 +553,11 @@ "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", @@ -587,6 +600,11 @@ "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", @@ -757,6 +775,53 @@ "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", @@ -836,8 +901,7 @@ "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=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "6.8.0", @@ -1351,6 +1415,11 @@ "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", @@ -1458,6 +1527,21 @@ "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", @@ -1470,12 +1554,22 @@ "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", @@ -1498,6 +1592,14 @@ "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", @@ -1727,6 +1829,21 @@ "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", @@ -1743,6 +1860,16 @@ "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", @@ -3060,6 +3187,60 @@ "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", @@ -3119,6 +3300,16 @@ } } }, + "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" + } + }, "tc-core-library-js": { "version": "github:appirio-tech/tc-core-library-js#df0b36c51cf80918194cbff777214b3c0cf5a151", "from": "github:appirio-tech/tc-core-library-js#v2.6.4", @@ -3164,6 +3355,21 @@ "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", diff --git a/package.json b/package.json index 5e38f26..2ab146d 100755 --- a/package.json +++ b/package.json @@ -16,16 +16,20 @@ "@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.15", - "moment": "^2.24.0", + "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" diff --git a/src/common/controller-helper.js b/src/common/controller-helper.js index e04eebb..08dc9db 100644 --- a/src/common/controller-helper.js +++ b/src/common/controller-helper.js @@ -32,7 +32,7 @@ function getControllerMethods (service) { * @param res the http response */ async function get (req, res) { - res.json(await service.get(req.params.id, req.auth)) + res.json(await service.get(req.params.id, req.auth, req.query)) } /** @@ -41,9 +41,9 @@ function getControllerMethods (service) { * @param res the http response */ async function search (req, res) { - const { items, meta } = await service.search(req.query, req.auth) - injectSearchMeta(res, meta) - res.json(items) + const result = await service.search(req.query, req.auth) + injectSearchMeta(req, res, result) + res.send(result.result) } /** @@ -98,7 +98,7 @@ function getSubControllerMethods (service) { * @param res the http response */ async function get (req, res) { - res.json(await service.get(req.params.id, req.auth, _.omit(req.params, 'id'))) + res.json(await service.get(req.params.id, req.auth, _.omit(req.params, 'id'), req.query)) } /** @@ -107,9 +107,9 @@ function getSubControllerMethods (service) { * @param res the http response */ async function search (req, res) { - const { items, meta } = await service.search(_.extend(req.query, req.params), req.auth) - injectSearchMeta(res, meta) - res.json(items) + const result = await service.search(_.extend(req.query, req.params), req.auth) + injectSearchMeta(req, res, result) + res.send(result.result) } /** diff --git a/src/common/errors.js b/src/common/errors.js index d1fd740..bdbad43 100644 --- a/src/common/errors.js +++ b/src/common/errors.js @@ -21,5 +21,6 @@ module.exports = { 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.') + newConflictError: msg => new AppError(409, msg || 'The entity does not exist.'), + 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..fec7e14 --- /dev/null +++ b/src/common/es-client.js @@ -0,0 +1,43 @@ +const config = require('config') +const AWS = require('aws-sdk') +const elasticsearch = require('elasticsearch') + +// Elasticsearch client +let esClient + +/** + * Get ES Client + * @return {Object} Elasticsearch Client Instance + */ +function getESClient () { + if (esClient) { + return esClient + } + const hosts = config.ES.HOST + const apiVersion = config.ES.API_VERSION + + if (!esClient) { + // AWS ES configuration is different from other providers + if (/.*amazonaws.*/.test(hosts)) { + esClient = new elasticsearch.Client({ + apiVersion, + hosts, + connectionClass: require('http-aws-es'), // eslint-disable-line global-require + amazonES: { + region: config.get('esConfig.AWS_REGION'), + credentials: new AWS.EnvironmentCredentials('AWS') + } + }) + } else { + esClient = new elasticsearch.Client({ + apiVersion, + hosts + }) + } + } + 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..d4b7e85 --- /dev/null +++ b/src/common/es-helper.js @@ -0,0 +1,731 @@ +const config = require('config') +const _ = require('lodash') +const logger = require('../common/logger') +const errors = require('./errors') +const groupApi = require('./group-api') +const esClient = require('./es-client').getESClient() + +const DOCUMENTS = config.ES.DOCUMENTS +const RESOURCES = Object.keys(DOCUMENTS) + +const SUB_DOCUMENTS = {} +const SUB_PROPERTIES = [] +_.forOwn(DOCUMENTS, (value, key) => { + if (value.userField) { + SUB_DOCUMENTS[key] = value + SUB_PROPERTIES.push(value.userField) + } +}) + +// mapping operation to topic +const OP_TO_TOPIC = { + create: config.UBAHN_CREATE_TOPIC, + update: config.UBAHN_UPDATE_TOPIC, + delete: config.UBAHN_DELETE_TOPIC +} + +// map model name to bus message resource if different +const MODEL_TO_RESOURCE = { + UsersSkill: 'userskill', + SkillsProvider: 'skillprovider', + AchievementsProvider: 'achievementprovider', + UsersAttribute: 'userattribute', + UsersRole: 'userrole' +} + +// resource filter config +const RESOURCE_FILTER = { + // independent resources + user: { + handle: { + resource: 'user', + queryField: 'handle' + }, + roleId: { + resource: 'userrole', + queryField: 'roleId' + } + // TODO usergroup/group resource is not implemented yet + // groupId: { + // resource: 'usergroup', + // queryField: 'groupId' + // } + }, + role: { + name: { + resource: 'role', + 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' + } + }, + achievement: { + achievementsProviderName: { + resource: 'achievementprovider', + queryField: 'name' + } + }, + userattribute: { + attributeName: { + resource: 'attribute', + queryField: 'name' + }, + attributeGroupName: { + resource: 'attributegroup', + queryField: 'name' + }, + attributeGroupId: { + resource: 'attributegroup', + queryField: 'id' + } + } +} + +// filter chaim 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: { + queryFielid: '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' + } +} + +/** + * 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() + } +} + +/** + * 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_DOCUMENTS)) { + const subDoc = SUB_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 subDoc = SUB_DOCUMENTS[resource] + const filterChain = FILTER_CHAIN[resource] + + // construct ES query + const esQuery = { + index: doc.userField ? userDoc.index : doc.index, + type: doc.userField ? userDoc.type : doc.type, + id: doc.userField ? params.userId : id + } + + if (resource === 'user') { + // handle enrich + if (!params.enrich) { + esQuery._source_excludes = SUB_PROPERTIES.join(',') + } + } else if (subDoc) { + esQuery._source_includes = subDoc.userField + } + + // query ES + const result = await esClient.getSource(esQuery) + + if (params.enrich && resource === 'user') { + const user = await enrichUser(result) + const groups = await groupApi.getGroups('user', user.id) + user.groups = groups + return user + } else if (subDoc) { + // find top sub doc by sub.id + const found = result[subDoc.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`) + } + } + 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) { + // TODO only current res filter + if (resFilters.length > 0) { + for (const filter of resFilters) { + const doc = DOCUMENTS[filter.resource] + const matchField = doc.userField ? `${doc.userField}.${doc.queryField}.keyword` : `${filter.queryField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + } +} + +/** + * 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: [] + } + } + } + } + + const matchField = `${filter.queryField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + return esQuery +} + +/** + * 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) { + 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) + + if (result.hits.total > 0) { + const value = result.hits.hits[0]._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) { + logger.debug(`Searching ES first: ${JSON.stringify(args, null, 2)}`) + // path and query parameters + const params = args[0] + const doc = DOCUMENTS[resource] + const userDoc = DOCUMENTS.user + const topSubDoc = SUB_DOCUMENTS[resource] + if (!params.page) { + params.page = 1 + } + if (!params.perPage) { + params.perPage = config.PAGE_SIZE + } + + 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) + } + } + } + } + + // construct ES query + const esQuery = { + index: doc.userField ? userDoc.index : doc.index, + type: doc.userField ? userDoc.type : doc.type, + size: params.perPage, + from: (params.page - 1) * params.perPage, // Es Index starts from 0 + body: { + query: { + bool: { + must: [] + } + } + } + } + + if (params.enrich && resource === 'user') { + // apply resolved pre-enrich filter values + if (enrichFilterResults.length > 0) { + for (const filter of enrichFilterResults) { + const matchField = `${filter.userField}.${filter.queryField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + } + // set the sub-resource filters which is in user index + _.forOwn(enrichFilters, (enFilter, resource) => { + _.forEach(enFilter, filter => { + if (filter.userField) { + const matchField = `${filter.userField}.${filter.queryField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + }) + }) + } else if (topSubDoc) { + // 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 = topSubDoc.userField + } + + // set pre res filter results + if (!params.enrich && preResFilterResults.length > 0) { + for (const filter of preResFilterResults) { + const matchField = filter.userField ? `${filter.userField}.${filter.queryField}.keyword` : `${filter.queryField}.keyword` + esQuery.body.query.bool.must.push({ + match: { + [matchField]: filter.value + } + }) + } + } + + 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 ${resource}: ${JSON.stringify(esQuery, null, 2)}`) + const docs = await esClient.search(esQuery) + if (docs.hits && docs.hits.total === 0) { + throw new Error('No data returns from ES query') + } + + 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', user.id) + user.groups = groups + } + } else if (topSubDoc) { + result = docs.hits.hits[0]._source[topSubDoc.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 { + result = docs.hits.hits.map(hit => hit._source) + } + + return { total: result.length, page: params.page, perPage: params.perPage, result } +} + +async function publishMessage (op, resource, result) { + const { postEvent } = require('./helper') + + if (!OP_TO_TOPIC[op]) { + logger.warn(`Invalid operation: ${op}`) + return + } + + 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: resource }, result)) +} + +/** + * Wrap QLDB methods with ES methods. Specifically read ES before QLDB for read operations, publish message + * after QLDB for write operations. + * @param methods QLDB CRUD methods + * @param Model the model to access + * @returns {{}} ES wrapped CRUD methods + */ +function wrapElasticSearchOp (methods, Model) { + logger.info('Decorating ES to API methods') + + // methods: create, search, patch, get, remove + const resource = getResource(Model.name) + + return _.mapValues(methods, func => { + if (func.name === 'search') { + return async (...args) => { + try { + return await searchElasticSearch(resource, ...args) + } catch (err) { + logger.logFullError(err) + // return error if enrich fails + if (resource === 'user' && args[0].enrich) { + throw errors.elasticSearchEnrichError(err.message) + } + const { items, meta } = await func(...args) + // 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, total: meta.total, result: items } + } + } + } else if (func.name === 'get') { + return async (...args) => { + if (args[3]) { + // merge query to params if exists. req.query was added at the end not to break the existing QLDB code. + args[2] = _.assign(args[2], args[3]) + } + try { + return await getFromElasticSearch(resource, ...args) + } catch (err) { + logger.logFullError(err) + // return error if enrich fails + if (resource === 'user' && args[2].enrich) { + throw errors.elasticSearchEnrichError(err.message) + } + const result = await func(...args) + return result + } + } + } else { + return async (...args) => { + const result = await func(...args) + try { + await publishMessage(func.name, resource, result) + } catch (err) { + logger.logFullError(err) + } + return result + } + } + }) +} + +module.exports = { + wrapElasticSearchOp +} diff --git a/src/common/group-api.js b/src/common/group-api.js new file mode 100644 index 0000000..da4b492 --- /dev/null +++ b/src/common/group-api.js @@ -0,0 +1,29 @@ +const config = require('config') +const _ = require('lodash') +const axios = require('axios') +const m2mAuth = require('tc-core-library-js').auth.m2m +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 (membershipType, memberId) { + const m2mToken = await getM2Mtoken() + const resp = await axios({ + method: 'get', + params: { membershipType, memberId }, + url: config.GROUP_API_URL, + headers: { Authorization: `Bearer ${m2mToken}` } + }) + return resp.data +} + +module.exports = { + getM2Mtoken, + getGroups +} diff --git a/src/common/helper.js b/src/common/helper.js index 9caf0c8..51ff870 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -4,11 +4,17 @@ const { 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'])) /** * convert json object to ion.js writer @@ -153,12 +159,58 @@ function checkIfExists (source, term) { } /** - * no paging in database, so only inject X-Total - * @param res the response - * @param meta the metadata + * 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 injectSearchMeta (res, meta) { - res.header({ 'X-Total': meta.total }) +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 totalPages = Math.ceil(result.total / 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', result.total) + 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) } /** @@ -173,6 +225,23 @@ function permissionCheck (auth, recordObj) { } } +/** + * 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, @@ -182,5 +251,6 @@ module.exports = { injectSearchMeta, getControllerMethods, getSubControllerMethods, - getServiceMethods + getServiceMethods, + postEvent } diff --git a/src/common/service-helper.js b/src/common/service-helper.js index 6486af9..00fdfaf 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -2,6 +2,7 @@ const joi = require('@hapi/joi') const appConst = require('../consts') const _ = require('lodash') const errors = require('./errors') +const esHelper = require('./es-helper') /** * make sure reference item exists @@ -141,10 +142,11 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil * get device by id * @param id the device id * @param auth the auth obj - * @param params the query params + * @param params the path parameters + * @param query the query parameters * @return {Promise} the db device */ - async function get (id, auth, params) { + async function get (id, auth, params, query) { let recordObj if (_.isNil(params) || _.isEmpty(params)) { recordObj = await models.DBHelper.get(Model, id) @@ -194,9 +196,11 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil await models.DBHelper.delete(Model, id, buildQueryByParams(params)) } - return { + const methods = { create, search, patch, get, remove } + + return esHelper.wrapElasticSearchOp(methods, Model) } module.exports = { From ef984b3c3b75cf806ce3d2f8249eadfa143f3008 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 1 Jun 2020 11:25:14 +0530 Subject: [PATCH 08/93] Update dev data insertion and deletion scripts to also insert and delete into elasticsearch --- scripts/constants.js | 84 +++++++++++++++++++ ...UsersAttribute.json => UserAttribute.json} | 0 scripts/db/dropAll.js | 10 +++ scripts/db/genData.js | 67 ++++++++++++++- 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 scripts/constants.js rename scripts/db/data/{UsersAttribute.json => UserAttribute.json} (100%) diff --git a/scripts/constants.js b/scripts/constants.js new file mode 100644 index 0000000..1dbfcbe --- /dev/null +++ b/scripts/constants.js @@ -0,0 +1,84 @@ +/** + * 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' + }, + userrole: { + propertyName: config.get('ES.DOCUMENTS.userrole.userField'), + relateKey: 'roleId' + }, + userskill: { + propertyName: config.get('ES.DOCUMENTS.userskill.userField'), + relateKey: 'skillId' + } +} + +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' +} + +module.exports = { + topResources, userResources, modelToESIndexMapping +} diff --git a/scripts/db/data/UsersAttribute.json b/scripts/db/data/UserAttribute.json similarity index 100% rename from scripts/db/data/UsersAttribute.json rename to scripts/db/data/UserAttribute.json diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js index eb2426f..1943c34 100644 --- a/scripts/db/dropAll.js +++ b/scripts/db/dropAll.js @@ -3,14 +3,24 @@ */ 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 { await models.DBHelper.drop(models[key]) + + if (_.includes(_.keys(topResources), esResourceName)) { + await client.indices.delete({ + index: topResources[esResourceName].index + }) + } } catch (e) { console.error(e) logger.warn(`drop table ${key} failed`) diff --git a/scripts/db/genData.js b/scripts/db/genData.js index 146112d..cae86ea 100644 --- a/scripts/db/genData.js +++ b/scripts/db/genData.js @@ -1,5 +1,57 @@ +const _ = require('lodash') const models = require('../../src/models') const logger = require('../../src/common/logger') +const { getESClient } = require('../../src/common/es-client') +const { topResources, userResources, 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 + }) + + 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' + }) + } + } +} /** * import test data @@ -7,7 +59,19 @@ const logger = require('../../src/common/logger') */ async function main () { await models.init() - const keys = Object.keys(models) + + 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) { @@ -16,6 +80,7 @@ async function main () { 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) { From 81a7e15bc010b0a0be9db1e9d28bd2a916f22662 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 1 Jun 2020 22:42:47 +0530 Subject: [PATCH 09/93] Fix issues with helper scripts --- README.md | 4 +++ package.json | 4 ++- scripts/db/data/Attribute.json | 8 +++--- scripts/db/data/User.json | 10 +++++-- scripts/db/data/UserAttribute.json | 46 ++++++++++++++++++++++++++++-- scripts/db/dropAll.js | 1 + 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 700b0ec..2b8d03e 100755 --- a/README.md +++ b/README.md @@ -52,6 +52,10 @@ Setup your Elasticsearch instance and ensure that it is up and running. 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 diff --git a/package.json b/package.json index 2ab146d..91dd2bf 100755 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "app.js", "scripts": { "start": "node app.js", - "lint": "standard \"**/*.js\"" + "lint": "standard \"**/*.js\"", + "insert-data": "node scripts/db/genData.js", + "delete-data": "node scripts/db/dropAll.js" }, "repository": { "type": "git", diff --git a/scripts/db/data/Attribute.json b/scripts/db/data/Attribute.json index 198a15e..27a62da 100644 --- a/scripts/db/data/Attribute.json +++ b/scripts/db/data/Attribute.json @@ -6,7 +6,7 @@ "createdBy": "tc-user", "updatedBy": null, "attributeGroupId": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", - "name": "attribute_02" + "name": "isAvailable" }, { "id": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", @@ -15,7 +15,7 @@ "createdBy": "tc-Admin", "updatedBy": null, "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", - "name": "attribute_02" + "name": "company" }, { "id": "ed1c98da-1876-49b9-a565-583fafd9cd4c", @@ -24,6 +24,6 @@ "createdBy": "tc-Admin", "updatedBy": null, "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", - "name": "attribute_01" + "name": "location" } -] \ No newline at end of file +] diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json index ed19fde..1e93dca 100644 --- a/scripts/db/data/User.json +++ b/scripts/db/data/User.json @@ -5,7 +5,9 @@ "updated": "2020-05-05T10:40:48.900Z", "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", - "handle": "handle_05" + "handle": "handle_05", + "firstName": "John", + "lastName": "Doe" }, { "id": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699", @@ -13,6 +15,8 @@ "updated": null, "createdBy": "tc-Copilot", "updatedBy": null, - "handle": "handle_01" + "handle": "handle_01", + "firstName": "Mark", + "lastName": "Robinson" } -] \ No newline at end of file +] diff --git a/scripts/db/data/UserAttribute.json b/scripts/db/data/UserAttribute.json index e196b57..937e00d 100644 --- a/scripts/db/data/UserAttribute.json +++ b/scripts/db/data/UserAttribute.json @@ -6,7 +6,7 @@ "createdBy": "tc-user", "updatedBy": null, "attributeId": "89b3c78f-d71b-4fc6-88cb-701c25e54dac", - "value": "1.23", + "value": "false", "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" }, { @@ -16,7 +16,47 @@ "createdBy": "tc-Admin", "updatedBy": null, "attributeId": "ed1c98da-1876-49b9-a565-583fafd9cd4c", - "value": "1.23", + "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": "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": "true", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "New York", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "Rhodes Fun Company", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" } -] \ No newline at end of file +] diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js index 1943c34..4adb586 100644 --- a/scripts/db/dropAll.js +++ b/scripts/db/dropAll.js @@ -1,6 +1,7 @@ /** * drop tables */ +const _ = require('lodash') const models = require('../../src/models') const logger = require('../../src/common/logger') const { topResources, modelToESIndexMapping } = require('../constants') From d71d59fb8f09c8b5c4bba101477f73169706618e Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 2 Jun 2020 12:45:19 +0530 Subject: [PATCH 10/93] Fix wrongly referenced configuration --- src/common/es-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/es-client.js b/src/common/es-client.js index fec7e14..53d4c28 100644 --- a/src/common/es-client.js +++ b/src/common/es-client.js @@ -24,7 +24,7 @@ function getESClient () { hosts, connectionClass: require('http-aws-es'), // eslint-disable-line global-require amazonES: { - region: config.get('esConfig.AWS_REGION'), + region: config.get('ES.AWS_REGION'), credentials: new AWS.EnvironmentCredentials('AWS') } }) From 5b0bdda57fc8486c048917b1cf0d36eb3fae8cdc Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 2 Jun 2020 13:00:46 +0530 Subject: [PATCH 11/93] Fix issue where elasticsearch connected to AWS throws credential errors --- README.md | 4 ++-- config/default.js | 4 ++-- src/models/DBHelper.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2b8d03e..703db6e 100755 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ Configuration for the application is at config/default.js and config/production. - VALID_ISSUERS: valid issuers for TC authentication - PAGE_SIZE: the default pagination limit - API_VERSION: the API version -- AWS_KEY: The AWS access key -- AWS_SECRET: The AWS secret key +- 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 diff --git a/config/default.js b/config/default.js index 3ddae92..b4f5a6d 100755 --- a/config/default.js +++ b/config/default.js @@ -13,8 +13,8 @@ module.exports = { PAGE_SIZE: process.env.PAGE_SIZE || 20, API_VERSION: process.env.API_VERSION || 'api/1.0', - AWS_KEY: process.env.AWS_KEY, - AWS_SECRET: process.env.AWS_SECRET, + 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', diff --git a/src/models/DBHelper.js b/src/models/DBHelper.js index 89a2da5..7862c4d 100644 --- a/src/models/DBHelper.js +++ b/src/models/DBHelper.js @@ -11,9 +11,9 @@ const errors = require('../common/errors') * the database instance */ const qldbInstance = new QLDB.PooledQldbDriver(config.DATABASE, { - region: config.AWS_REGION, - secretAccessKey: config.AWS_SECRET, - accessKeyId: config.AWS_KEY + accessKeyId: config.AWS_ACCESS_KEY_ID, + secretAccessKey: config.AWS_SECRET_ACCESS_KEY, + region: config.AWS_REGION }) /** From ac2e77978d05e00f1201b1b8be05193f5c1f0ffd Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 2 Jun 2020 13:45:25 +0530 Subject: [PATCH 12/93] Fix issue where tonyj was not administrator --- src/common/helper.js | 2 +- src/common/service-helper.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index 51ff870..06ecb1d 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -219,7 +219,7 @@ function injectSearchMeta (req, res, result) { * @param recordObj the record object */ function permissionCheck (auth, recordObj) { - if (auth && auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin]) && + if (auth && auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator]) && recordObj.createdBy !== getAuthUser(auth)) { throw errors.newPermissionError('You are not allowed to perform this action') } diff --git a/src/common/service-helper.js b/src/common/service-helper.js index 00fdfaf..2d64c5f 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -172,7 +172,7 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil // user token // for non-admin users, this endpoint will only return entities that the user has created. - if (auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin])) { + if (auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { dbQueries.push(`${Model.tableName}.createdBy = '${getAuthUser(auth)}'`) } const items = await models.DBHelper.find(Model, dbQueries) From 7d2cad82150c7bc5eecf29274eabf6ca6b39af51 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 2 Jun 2020 16:26:13 +0530 Subject: [PATCH 13/93] Fix issue where elasticsearch connection would fail --- README.md | 1 - config/default.js | 1 - src/common/es-client.js | 16 +++++++--------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 703db6e..7dcce81 100755 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ Configuration for the application is at config/default.js and config/production. - UBAHN_UPDATE_TOPIC: Kafka topic for update message - UBAHN_DELETE_TOPIC: Kafka topic for delete message - ES.HOST: Elasticsearch host -- ES.AWS_REGION: The Amazon region to use when using AWS Elasticsearch service - ES.API_VERSION: Elasticsearch API version - ES.DOCUMENTS: Elasticsearch index, type and id mapping for resources. diff --git a/config/default.js b/config/default.js index b4f5a6d..7cd79a9 100755 --- a/config/default.js +++ b/config/default.js @@ -39,7 +39,6 @@ module.exports = { // ElasticSearch ES: { HOST: process.env.ES_HOST || 'localhost:9200', - AWS_REGION: process.env.AWS_REGION || 'us-east-1', // AWS Region to be used if we use AWS ES API_VERSION: process.env.ES_API_VERSION || '6.8', // es mapping: _index, _type, _id DOCUMENTS: { diff --git a/src/common/es-client.js b/src/common/es-client.js index 53d4c28..2ffc3f1 100644 --- a/src/common/es-client.js +++ b/src/common/es-client.js @@ -2,6 +2,8 @@ const config = require('config') const AWS = require('aws-sdk') const elasticsearch = require('elasticsearch') +AWS.config.region = config.AWS_REGION + // Elasticsearch client let esClient @@ -13,25 +15,21 @@ function getESClient () { if (esClient) { return esClient } - const hosts = config.ES.HOST + const host = config.ES.HOST const apiVersion = config.ES.API_VERSION if (!esClient) { // AWS ES configuration is different from other providers - if (/.*amazonaws.*/.test(hosts)) { + if (/.*amazonaws.*/.test(host)) { esClient = new elasticsearch.Client({ apiVersion, - hosts, - connectionClass: require('http-aws-es'), // eslint-disable-line global-require - amazonES: { - region: config.get('ES.AWS_REGION'), - credentials: new AWS.EnvironmentCredentials('AWS') - } + host, + connectionClass: require('http-aws-es') // eslint-disable-line global-require }) } else { esClient = new elasticsearch.Client({ apiVersion, - hosts + host }) } } From 77aea225bd09072037ea215cedbbcd9d8d17b110 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 3 Jun 2020 15:18:10 +0530 Subject: [PATCH 14/93] Fix issues reported by reviewer --- docs/swagger.yaml | 248 ++++++++++++++++++++++++++++++++ src/common/controller-helper.js | 2 + src/common/es-helper.js | 150 ++++++++++++------- src/common/helper.js | 4 +- 4 files changed, 354 insertions(+), 50 deletions(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c6b0d9b..ecc1567 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -69,6 +69,125 @@ paths: required: false type: "string" format: "UUID" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + - 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" @@ -112,6 +231,125 @@ paths: required: false type: "string" format: "UUID" + - name: "enrich" + in: "query" + description: "Enrich all child resources under the user" + required: false + type: "boolean" + - 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" @@ -171,6 +409,11 @@ paths: 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" @@ -203,6 +446,11 @@ paths: 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" diff --git a/src/common/controller-helper.js b/src/common/controller-helper.js index 08dc9db..a00dd76 100644 --- a/src/common/controller-helper.js +++ b/src/common/controller-helper.js @@ -98,6 +98,8 @@ function getSubControllerMethods (service) { * @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)) } diff --git a/src/common/es-helper.js b/src/common/es-helper.js index d4b7e85..f329a27 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -3,6 +3,7 @@ const _ = require('lodash') const logger = require('../common/logger') const errors = require('./errors') const groupApi = require('./group-api') +const appConst = require('../consts') const esClient = require('./es-client').getESClient() const DOCUMENTS = config.ES.DOCUMENTS @@ -20,8 +21,8 @@ _.forOwn(DOCUMENTS, (value, key) => { // mapping operation to topic const OP_TO_TOPIC = { create: config.UBAHN_CREATE_TOPIC, - update: config.UBAHN_UPDATE_TOPIC, - delete: config.UBAHN_DELETE_TOPIC + patch: config.UBAHN_UPDATE_TOPIC, + remove: config.UBAHN_DELETE_TOPIC } // map model name to bus message resource if different @@ -334,6 +335,8 @@ async function getFromElasticSearch (resource, ...args) { esQuery._source_includes = subDoc.userField } + logger.debug(`ES query for get ${resource}: ${JSON.stringify(esQuery, null, 2)}`) + // query ES const result = await esClient.getSource(esQuery) @@ -401,7 +404,10 @@ function setResourceFilterToEsQuery (resFilters, esQuery) { if (resFilters.length > 0) { for (const filter of resFilters) { const doc = DOCUMENTS[filter.resource] - const matchField = doc.userField ? `${doc.userField}.${doc.queryField}.keyword` : `${filter.queryField}.keyword` + let matchField = doc.userField ? `${doc.userField}.${doc.queryField}` : `${filter.queryField}` + if (filter.queryField !== 'name') { + matchField = matchField + '.keyword' + } esQuery.body.query.bool.must.push({ match: { [matchField]: filter.value @@ -411,6 +417,37 @@ function setResourceFilterToEsQuery (resFilters, esQuery) { } } +/** + * 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') { + 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 +} + /** * Build ES query from given filter. * @param filter @@ -424,19 +461,16 @@ function buildEsQueryFromFilter (filter) { body: { query: { bool: { - must: [] + must: [], + should: [], + minimum_should_match: 0 } } } } - const matchField = `${filter.queryField}.keyword` - esQuery.body.query.bool.must.push({ - match: { - [matchField]: filter.value - } - }) - return esQuery + const matchField = `${filter.queryField}` + return setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) } /** @@ -464,7 +498,13 @@ async function resolveResFilter (filter, initialRes) { const result = await esClient.search(esQuery) if (result.hits.total > 0) { - const value = result.hits.hits[0]._source.id + // this value can be array + let value + if (result.hits.total === 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, @@ -514,9 +554,11 @@ function applySubResFilters (results, preResFilterResults, ownResFilters, perPag * @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 topSubDoc = SUB_DOCUMENTS[resource] @@ -562,37 +604,48 @@ async function searchElasticSearch (resource, ...args) { body: { query: { bool: { - must: [] + must: [], + should: [], + minimum_should_match: 0 } } } } - if (params.enrich && resource === 'user') { - // apply resolved pre-enrich filter values - if (enrichFilterResults.length > 0) { - for (const filter of enrichFilterResults) { - const matchField = `${filter.userField}.${filter.queryField}.keyword` - esQuery.body.query.bool.must.push({ - match: { - [matchField]: filter.value - } - }) + // for non-admin, only return entities that the user created + if (authUser.roles && !checkIfExists(authUser.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { + setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') + } + + if (resource === 'user') { + if (params.enrich) { + // 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) { - const matchField = `${filter.userField}.${filter.queryField}.keyword` - esQuery.body.query.bool.must.push({ - match: { - [matchField]: filter.value + // 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_PROPERTIES.join(',') + } } else if (topSubDoc) { // add userId match const userFC = FILTER_CHAIN.user @@ -608,12 +661,8 @@ async function searchElasticSearch (resource, ...args) { // set pre res filter results if (!params.enrich && preResFilterResults.length > 0) { for (const filter of preResFilterResults) { - const matchField = filter.userField ? `${filter.userField}.${filter.queryField}.keyword` : `${filter.queryField}.keyword` - esQuery.body.query.bool.must.push({ - match: { - [matchField]: filter.value - } - }) + const matchField = filter.userField ? `${filter.userField}.${filter.queryField}` : `${filter.queryField}` + setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) } } @@ -623,7 +672,7 @@ async function searchElasticSearch (resource, ...args) { setResourceFilterToEsQuery(ownResFilters, esQuery) } - logger.debug(`ES query for ${resource}: ${JSON.stringify(esQuery, null, 2)}`) + logger.debug(`ES query for search ${resource}: ${JSON.stringify(esQuery, null, 2)}`) const docs = await esClient.search(esQuery) if (docs.hits && docs.hits.total === 0) { throw new Error('No data returns from ES query') @@ -647,7 +696,7 @@ async function searchElasticSearch (resource, ...args) { result = docs.hits.hits.map(hit => hit._source) } - return { total: result.length, page: params.page, perPage: params.perPage, result } + return { total: docs.hits.total, page: params.page, perPage: params.perPage, result } } async function publishMessage (op, resource, result) { @@ -683,11 +732,11 @@ function wrapElasticSearchOp (methods, Model) { try { return await searchElasticSearch(resource, ...args) } catch (err) { - logger.logFullError(err) // return error if enrich fails if (resource === 'user' && args[0].enrich) { throw errors.elasticSearchEnrichError(err.message) } + logger.logFullError(err) const { items, meta } = await func(...args) // return fromDB:true to indicate it is got from db, // and response headers ('X-Total', 'X-Page', etc.) are not set in this case @@ -695,19 +744,24 @@ function wrapElasticSearchOp (methods, Model) { } } } else if (func.name === 'get') { + const { permissionCheck } = require('./helper') return async (...args) => { if (args[3]) { // merge query to params if exists. req.query was added at the end not to break the existing QLDB code. args[2] = _.assign(args[2], args[3]) } try { - return await getFromElasticSearch(resource, ...args) + const result = await getFromElasticSearch(resource, ...args) + // check permission + const authUser = args[1] + permissionCheck(authUser, result) + return result } catch (err) { - logger.logFullError(err) - // return error if enrich fails - if (resource === 'user' && args[2].enrich) { + // return error if enrich fails or permission fails + if ((resource === 'user' && args[2].enrich) || (err.status && err.status === 403)) { throw errors.elasticSearchEnrichError(err.message) } + logger.logFullError(err) const result = await func(...args) return result } diff --git a/src/common/helper.js b/src/common/helper.js index 06ecb1d..a4609f8 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -183,10 +183,10 @@ function injectSearchMeta (req, res, result) { const totalPages = Math.ceil(result.total / result.perPage) if (result.page > 1) { - res.set('X-Prev-Page', 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-Next-Page', +result.page + 1) } res.set('X-Page', result.page) res.set('X-Per-Page', result.perPage) From a2ed8c1496aede6b5a6238fb97e7edb0486aa932 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 4 Jun 2020 17:14:16 +0530 Subject: [PATCH 15/93] Fix issue where deleted record event would not be published to bus api --- src/common/es-helper.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index f329a27..f158006 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -768,7 +768,13 @@ function wrapElasticSearchOp (methods, Model) { } } else { return async (...args) => { - const result = await func(...args) + let result = await func(...args) + // remove action returns undefined, pass id to elasticsearch + if (func.name === 'remove') { + result = { + id: args[0] + } + } try { await publishMessage(func.name, resource, result) } catch (err) { From f2a1c175d88087ebb613994557b00b600e29f717 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Fri, 5 Jun 2020 19:57:10 +0530 Subject: [PATCH 16/93] Delete data script should clear data instead of dropping tables because QLDB does not drop tables, only deactivates them --- scripts/db/dropAll.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js index 4adb586..0bee02a 100644 --- a/scripts/db/dropAll.js +++ b/scripts/db/dropAll.js @@ -15,7 +15,7 @@ async function main () { if (models[key].tableName) { const esResourceName = modelToESIndexMapping[key] try { - await models.DBHelper.drop(models[key]) + await models.DBHelper.clear(models[key]) if (_.includes(_.keys(topResources), esResourceName)) { await client.indices.delete({ From 0d2f1c5aa7cea99f7c7afdd6c8f026927590cb16 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sat, 6 Jun 2020 19:09:39 +0530 Subject: [PATCH 17/93] Support first name and last name for users and provide mock data for user role / title --- docs/swagger.yaml | 24 ++++++++++++++++++++++++ scripts/db/data/Attribute.json | 11 ++++++++++- scripts/db/data/UserAttribute.json | 20 ++++++++++++++++++++ src/models/User.js | 2 ++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ecc1567..e93fb3c 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -3681,6 +3681,8 @@ definitions: - type: "object" required: - "handle" + - "firstName" + - "lastName" - "id" properties: id: @@ -3690,6 +3692,12 @@ definitions: 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: @@ -3789,17 +3797,33 @@ definitions: 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" diff --git a/scripts/db/data/Attribute.json b/scripts/db/data/Attribute.json index 27a62da..b1b835b 100644 --- a/scripts/db/data/Attribute.json +++ b/scripts/db/data/Attribute.json @@ -5,7 +5,7 @@ "updated": null, "createdBy": "tc-user", "updatedBy": null, - "attributeGroupId": "7ce54e9c-5a2a-4c63-9f53-b854234f6bb2", + "attributeGroupId": "84634bbd-8191-40cf-a03e-9962d7e39fda", "name": "isAvailable" }, { @@ -25,5 +25,14 @@ "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" } ] diff --git a/scripts/db/data/UserAttribute.json b/scripts/db/data/UserAttribute.json index 937e00d..4a74230 100644 --- a/scripts/db/data/UserAttribute.json +++ b/scripts/db/data/UserAttribute.json @@ -29,6 +29,16 @@ "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": "20721207-c868-4ee4-8ac4-59db683b7bce", "created": "2020-05-13T08:48:30.541Z", @@ -58,5 +68,15 @@ "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" } ] diff --git a/src/models/User.js b/src/models/User.js index f63a7d5..f089ccb 100644 --- a/src/models/User.js +++ b/src/models/User.js @@ -7,6 +7,8 @@ class User extends RecordObject { constructor () { super() this.handle = null + this.firstName = null + this.lastName = null } } From d4a726371ec39066e9d23be64d03682134a921d9 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sat, 6 Jun 2020 23:18:55 +0530 Subject: [PATCH 18/93] Support location, availability, skills and achievement filters needed by UI --- docs/UBahn_API.postman_collection.json | 1470 ++++++++++++++++++------ scripts/db/data/User.json | 2 +- src/common/es-helper.js | 179 ++- 3 files changed, 1287 insertions(+), 364 deletions(-) diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index 1204bed..45ba8a3 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -1,8 +1,8 @@ { "info": { - "_postman_id": "5c1cb83a-4ae3-4e5b-b89d-d59005e44b70", + "_postman_id": "8248607f-018f-4ac2-b088-edb49f421ff0", "name": "UBahn_API", - "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { @@ -14,7 +14,7 @@ { "listen": "test", "script": { - "id": "9be82782-7a85-4db7-b4f3-aa3f739abb85", + "id": "b469546c-8293-4bb3-afb2-45e14b093bc8", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -42,7 +42,15 @@ "mode": "raw", "raw": "{\n\t\"handle\":\"handle_01\"\n}" }, - "url": "{{HOST}}/users" + "url": { + "raw": "{{HOST}}/users", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ] + } }, "response": [] }, @@ -57,10 +65,6 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/users/{{userId}}?enrich=true", "host": [ @@ -101,7 +105,16 @@ "mode": "raw", "raw": "{\n\t\"handle\":\"handle_05\"\n}" }, - "url": "{{HOST}}/users/{{userId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } }, "response": [] }, @@ -122,11 +135,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } }, "response": [] }, @@ -141,10 +159,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/users?enrich=true&role.name=role-2", "host": [ @@ -197,6 +211,98 @@ }, "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": { @@ -208,11 +314,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users" + "url": { + "raw": "{{HOST}}/users", + "host": [ + "{{HOST}}" + ], + "path": [ + "users" + ] + } }, "response": [] }, @@ -237,11 +347,21 @@ "mode": "raw", "raw": "" }, - "url": "{{HOST}}/users/{{userId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "roles", @@ -252,7 +372,7 @@ { "listen": "test", "script": { - "id": "ff8c198b-a5fc-421c-8930-ff4f7ac2cc29", + "id": "d8f3f5d1-c62b-4dde-ae13-a6c644917400", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"roleId\", rsp.id);" @@ -278,9 +398,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"Admin\"\n}" + "raw": "{\n\t\"name\":\"Admin\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/roles" + "url": { + "raw": "{{HOST}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles" + ] + } }, "response": [] }, @@ -295,11 +426,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -322,9 +458,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"Admin02\"\n}" + "raw": "{\n\t\"name\":\"Admin02\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -345,11 +493,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -364,10 +517,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/roles?name=m", "host": [ @@ -397,11 +546,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/roles" + "url": { + "raw": "{{HOST}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles" + ] + } }, "response": [] }, @@ -424,13 +577,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "roles", + "{{roleId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "usersRoles", @@ -441,7 +607,7 @@ { "listen": "test", "script": { - "id": "aba26e50-293b-482e-b023-47adba0f4ff3", + "id": "4a4fc48b-a080-4ba1-985f-233099d955ca", "exec": [ "" ], @@ -466,9 +632,22 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"roleId\":\"{{roleId}}\"\n}" + "raw": "{\n\t\"roleId\":\"{{roleId}}\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/roles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } }, "response": [] }, @@ -483,11 +662,18 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -510,9 +696,23 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"roleId\":\"8607ddb3-abf6-4512-a618-c60d4771174b\"\n}" + "raw": "{\n\t\"roleId\":\"8607ddb3-abf6-4512-a618-c60d4771174b\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -533,11 +733,18 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } }, "response": [] }, @@ -552,11 +759,17 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/roles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } }, "response": [] }, @@ -571,11 +784,17 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/roles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles" + ] + } }, "response": [] }, @@ -598,13 +817,28 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/roles/{{roleId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/roles/{{roleId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "roles", + "{{roleId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "organizations", @@ -615,7 +849,7 @@ { "listen": "test", "script": { - "id": "65a0007b-379b-42f6-801e-cdd2d195a586", + "id": "b4080e98-c6a7-452a-9f42-2b0d78b606d7", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"organizationId\", rsp.id);" @@ -641,9 +875,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"organization_01\"\n}" + "raw": "{\n\t\"name\":\"organization_01\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/organizations" + "url": { + "raw": "{{HOST}}/organizations", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations" + ] + } }, "response": [] }, @@ -658,11 +903,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/organizations/603d4264-cdb0-47f1-914e-f053abc60422" + "url": { + "raw": "{{HOST}}/organizations/603d4264-cdb0-47f1-914e-f053abc60422", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "603d4264-cdb0-47f1-914e-f053abc60422" + ] + } }, "response": [] }, @@ -685,9 +935,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"organization_update\"\n}" + "raw": "{\n\t\"name\":\"organization_update\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/organizations/{{organizationId}}" + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } }, "response": [] }, @@ -708,11 +970,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/organizations/{{organizationId}}" + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } }, "response": [] }, @@ -727,10 +994,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/organizations?name=o", "host": [ @@ -760,11 +1023,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/organizations" + "url": { + "raw": "{{HOST}}/organizations", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations" + ] + } }, "response": [] }, @@ -787,13 +1054,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/organizations/{{organizationId}}" + "url": { + "raw": "{{HOST}}/organizations/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "organizations", + "{{organizationId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "skillsProviders", @@ -804,7 +1084,7 @@ { "listen": "test", "script": { - "id": "624175cd-8315-42a3-a316-32a34fbcfadf", + "id": "d5d06aca-cc7c-4a0c-a602-44b0768bf509", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillsProviderId\", rsp.id);" @@ -830,9 +1110,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skillsProviders_01\"\n}" + "raw": "{\n\t\"name\":\"skillsProviders_01\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skillsProviders" + "url": { + "raw": "{{HOST}}/skillsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders" + ] + } }, "response": [] }, @@ -847,11 +1138,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } }, "response": [] }, @@ -874,9 +1170,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skillsProviders_update\"\n}" + "raw": "{\n\t\"name\":\"skillsProviders_update\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } }, "response": [] }, @@ -897,11 +1205,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } }, "response": [] }, @@ -916,10 +1229,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/skillsProviders?name=ski", "host": [ @@ -949,11 +1258,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skillsProviders" + "url": { + "raw": "{{HOST}}/skillsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders" + ] + } }, "response": [] }, @@ -976,13 +1289,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skillsProviders/{{skillsProviderId}}" + "url": { + "raw": "{{HOST}}/skillsProviders/{{skillsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skillsProviders", + "{{skillsProviderId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "skills", @@ -993,7 +1319,7 @@ { "listen": "test", "script": { - "id": "c7176c3b-5fea-4236-96cb-cfd7ff1383ee", + "id": "79033f51-2602-4303-bd92-898814d4b6fd", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillId\", rsp.id);" @@ -1019,9 +1345,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"skillProviderId\":\"{{skillsProviderId}}\",\n\t\"name\":\"jump\",\n\t\"uri\":\"http://www.google.com\",\n\t\"externalId\":\"externalId\"\n}" + "raw": "{\n\t\"skillProviderId\":\"{{skillsProviderId}}\",\n\t\"name\":\"jump\",\n\t\"uri\":\"http://www.google.com\",\n\t\"externalId\":\"externalId\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skills" + "url": { + "raw": "{{HOST}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills" + ] + } }, "response": [] }, @@ -1036,11 +1373,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1063,9 +1405,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"skill_name_update\"\n}" + "raw": "{\n\t\"name\":\"skill_name_update\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1086,11 +1440,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1105,10 +1464,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/skills", "host": [ @@ -1139,11 +1494,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/skills" + "url": { + "raw": "{{HOST}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills" + ] + } }, "response": [] }, @@ -1166,13 +1525,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "skills", + "{{skillId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "usersSkills", @@ -1183,7 +1555,7 @@ { "listen": "test", "script": { - "id": "c1c418d7-bcda-4fd0-95ec-62050e002ea7", + "id": "c864cd20-ea0c-40c6-9c6b-f50dbbea2cfb", "exec": [ "" ], @@ -1208,9 +1580,22 @@ ], "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}" + "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": "{{HOST}}/users/{{userId}}/skills" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ] + } }, "response": [] }, @@ -1225,11 +1610,18 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1252,9 +1644,23 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"metricValue\":\"4.5L\"\n}" + "raw": "{\n\t\"metricValue\":\"4.5L\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1275,11 +1681,18 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } }, "response": [] }, @@ -1294,10 +1707,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/users/{{userId}}/skills?skillName=skill-2", "host": [ @@ -1329,11 +1738,17 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/skills" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills" + ] + } }, "response": [] }, @@ -1356,13 +1771,28 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/skills/{{skillId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/skills/{{skillId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "skills", + "{{skillId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "externalProfiles", @@ -1373,7 +1803,7 @@ { "listen": "test", "script": { - "id": "84cfec46-193e-4705-9f37-2f1cfae42cd7", + "id": "a61bd92d-8189-4005-ae58-d28b4b475aca", "exec": [ "" ], @@ -1398,9 +1828,22 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"uri\":\"http://uri.com/uri\"\n}" + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"uri\":\"http://uri.com/uri\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } }, "response": [] }, @@ -1415,11 +1858,18 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } }, "response": [] }, @@ -1442,9 +1892,23 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"uri\":\"http://www.new.com/new-uri\"\n}" + "raw": "{\n\t\"uri\":\"http://www.new.com/new-uri\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } }, "response": [] }, @@ -1465,11 +1929,18 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } }, "response": [] }, @@ -1484,10 +1955,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/users/{{userId}}/externalProfiles?organizationName=organization", "host": [ @@ -1519,11 +1986,17 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } }, "response": [] }, @@ -1546,13 +2019,28 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "achievementsProviders", @@ -1563,7 +2051,7 @@ { "listen": "test", "script": { - "id": "d52dbd5e-7b35-42c9-b306-6dade4498eec", + "id": "0cd87552-e2d3-4510-8ad3-a2bd411d06e2", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"achievementsProviderId\", rsp.id);" @@ -1589,9 +2077,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"achievementsProviders_02\"\n}" + "raw": "{\n\t\"name\":\"achievementsProviders_02\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/achievementsProviders" + "url": { + "raw": "{{HOST}}/achievementsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders" + ] + } }, "response": [] }, @@ -1606,11 +2105,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } }, "response": [] }, @@ -1633,9 +2137,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"achievementsProviders_update\"\n}" + "raw": "{\n\t\"name\":\"achievementsProviders_update\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } }, "response": [] }, @@ -1656,11 +2172,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } }, "response": [] }, @@ -1675,10 +2196,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/achievementsProviders?name=a", "host": [ @@ -1708,11 +2225,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/achievementsProviders" + "url": { + "raw": "{{HOST}}/achievementsProviders", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders" + ] + } }, "response": [] }, @@ -1735,13 +2256,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/achievementsProviders/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "achievementsProviders", + "{{achievementsProviderId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "achievements", @@ -1752,7 +2286,7 @@ { "listen": "test", "script": { - "id": "4e1eecd6-6e64-4e1c-a3bb-b02430030fe2", + "id": "0183c828-5145-468a-b2ce-29b39722f573", "exec": [ "" ], @@ -1777,9 +2311,22 @@ ], "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}" + "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": "{{HOST}}/users/{{userId}}/achievements" + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements" + ] + } }, "response": [] }, @@ -1794,11 +2341,18 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/achievements/7b4f98b1-5831-45fe-a71f-8454d11eb8e8" + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/7b4f98b1-5831-45fe-a71f-8454d11eb8e8", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "7b4f98b1-5831-45fe-a71f-8454d11eb8e8" + ] + } }, "response": [] }, @@ -1821,9 +2375,23 @@ ], "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}" + "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": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "{{achievementsProviderId}}" + ] + } }, "response": [] }, @@ -1844,11 +2412,18 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/achievements/{{achievementsProviderId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "achievements", + "{{achievementsProviderId}}" + ] + } }, "response": [] }, @@ -1863,12 +2438,8 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { - "raw": "{{HOST}}/users/{{userId}}/achievements?", + "raw": "{{HOST}}/users/{{userId}}/achievements", "host": [ "{{HOST}}" ], @@ -1899,11 +2470,17 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles" + ] + } }, "response": [] }, @@ -1926,13 +2503,28 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/externalProfiles/{{organizationId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "externalProfiles", + "{{organizationId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "attributeGroups", @@ -1943,7 +2535,7 @@ { "listen": "test", "script": { - "id": "bedc5acf-f088-400c-8b89-8f1ae915b0e0", + "id": "5117cfff-e15d-4ce9-958b-6f0ece556de2", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeGroupId\", rsp.id);" @@ -1969,9 +2561,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"name\":\"attributeGroup_01\"\n}" + "raw": "{\n\t\"organizationId\":\"{{organizationId}}\",\n\t\"name\":\"attributeGroup_01\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributeGroups" + "url": { + "raw": "{{HOST}}/attributeGroups", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ] + } }, "response": [] }, @@ -1986,11 +2589,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } }, "response": [] }, @@ -2013,9 +2621,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"group 03\"\n}" + "raw": "{\n\t\"name\":\"group 03\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } }, "response": [] }, @@ -2036,11 +2656,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } }, "response": [] }, @@ -2055,12 +2680,8 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { - "raw": "{{HOST}}/attributeGroups?", + "raw": "{{HOST}}/attributeGroups", "host": [ "{{HOST}}" ], @@ -2094,11 +2715,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributeGroups" + "url": { + "raw": "{{HOST}}/attributeGroups", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups" + ] + } }, "response": [] }, @@ -2121,13 +2746,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributeGroups/{{attributeGroupId}}" + "url": { + "raw": "{{HOST}}/attributeGroups/{{attributeGroupId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributeGroups", + "{{attributeGroupId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "attributes", @@ -2138,7 +2776,7 @@ { "listen": "test", "script": { - "id": "6b14dfd2-8f67-4806-8c55-b468e78cbf52", + "id": "fff8a571-407c-42e9-b7b6-fe50896b0d92", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeId\", rsp.id);" @@ -2164,9 +2802,20 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"attributeGroupId\":\"{{attributeGroupId}}\",\n\t\"name\":\"attribute_02\"\n}" + "raw": "{\n\t\"attributeGroupId\":\"{{attributeGroupId}}\",\n\t\"name\":\"attribute_02\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributes" + "url": { + "raw": "{{HOST}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes" + ] + } }, "response": [] }, @@ -2181,11 +2830,16 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } }, "response": [] }, @@ -2208,9 +2862,21 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"name\":\"attr-04\"\n}" + "raw": "{\n\t\"name\":\"attr-04\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } }, "response": [] }, @@ -2231,11 +2897,16 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } }, "response": [] }, @@ -2250,10 +2921,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/attributes?name=attr", "host": [ @@ -2288,11 +2955,15 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/attributes" + "url": { + "raw": "{{HOST}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes" + ] + } }, "response": [] }, @@ -2315,13 +2986,26 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "attributes", + "{{attributeId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "userAttributes", @@ -2332,7 +3016,7 @@ { "listen": "test", "script": { - "id": "f7a05416-fcef-4107-9c35-e83eccbc6f07", + "id": "9f864ea3-c055-4ff1-97b8-1c80094d3710", "exec": [ "" ], @@ -2357,9 +3041,22 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"attributeId\":\"{{attributeId}}\",\n\t\"value\":\"1.23\"\n}" + "raw": "{\n\t\"attributeId\":\"{{attributeId}}\",\n\t\"value\":\"1.23\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/attributes" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ] + } }, "response": [] }, @@ -2374,11 +3071,18 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/attributes/b746ef65-336d-4846-b02a-f25f6cff72c9" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/b746ef65-336d-4846-b02a-f25f6cff72c9", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "b746ef65-336d-4846-b02a-f25f6cff72c9" + ] + } }, "response": [] }, @@ -2401,9 +3105,23 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"value\":\"2.56\"\n}" + "raw": "{\n\t\"value\":\"2.56\"\n}", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } }, "response": [] }, @@ -2424,11 +3142,18 @@ "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } }, "response": [] }, @@ -2443,10 +3168,6 @@ "type": "text" } ], - "body": { - "mode": "raw", - "raw": "" - }, "url": { "raw": "{{HOST}}/users/{{userId}}/attributes?attributeName=attribute&attributeGroupName=attributegroup&attributeGroupId=720c34f9-0fd4-46fd-9293-4a8cfdcd3e96", "host": [ @@ -2486,11 +3207,17 @@ "value": "Bearer {{token}}" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "{{HOST}}/users/{{userId}}/attributes" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes" + ] + } }, "response": [] }, @@ -2513,13 +3240,28 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, - "url": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}" + "url": { + "raw": "{{HOST}}/users/{{userId}}/attributes/{{attributeId}}", + "host": [ + "{{HOST}}" + ], + "path": [ + "users", + "{{userId}}", + "attributes", + "{{attributeId}}" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} }, { "name": "Group API", @@ -2535,15 +3277,25 @@ "value": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik5VSkZORGd4UlRVME5EWTBOVVkzTlRkR05qTXlRamxETmpOQk5UYzVRVUV3UlRFeU56TTJRUSJ9.eyJpc3MiOiJodHRwczovL3RvcGNvZGVyLWRldi5hdXRoMC5jb20vIiwic3ViIjoibWFFMm1hQlN2OWZSVkhqU2xDMzFMRlpTcTZWaGhacUNAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vbTJtLnRvcGNvZGVyLWRldi5jb20vIiwiaWF0IjoxNTkwNDczODY5LCJleHAiOjE1OTA1NjAyNjksImF6cCI6Im1hRTJtYUJTdjlmUlZIalNsQzMxTEZaU3E2VmhoWnFDIiwic2NvcGUiOiJyZWFkOmNoYWxsZW5nZXMgd3JpdGU6Y2hhbGxlbmdlcyByZWFkOmdyb3VwcyB1cGRhdGU6c3VibWlzc2lvbiByZWFkOnN1Ym1pc3Npb24gZGVsZXRlOnN1Ym1pc3Npb24gY3JlYXRlOnN1Ym1pc3Npb24gYWxsOnN1Ym1pc3Npb24gdXBkYXRlOnJldmlld190eXBlIHJlYWQ6cmV2aWV3X3R5cGUgZGVsZXRlOnJldmlld190eXBlIGFsbDpyZXZpZXdfdHlwZSB1cGRhdGU6cmV2aWV3X3N1bW1hdGlvbiByZWFkOnJldmlld19zdW1tYXRpb24gZGVsZXRlOnJldmlld19zdW1tYXRpb24gY3JlYXRlOnJldmlld19zdW1tYXRpb24gYWxsOnJldmlld19zdW1tYXRpb24gdXBkYXRlOnJldmlldyByZWFkOnJldmlldyBkZWxldGU6cmV2aWV3IGNyZWF0ZTpyZXZpZXcgYWxsOnJldmlldyByZWFkOmJ1c190b3BpY3Mgd3JpdGU6YnVzX2FwaSByZWFkOnVzZXJfcHJvZmlsZXMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.MAhr5GZaOsx5nmkE1wY4QQP2TQWzegw64TJHfth1-ShRJpy7bPG5LPCmY4YDWphi7K4IrjyKril8ZY9BdbDKnpzXbviZvEZzO8eAXgPlemmCoLik8x_DaZjYJ1CjJlEzLIkh4vtJ-H5lxYT36KTTOzN6z6nkvVk8kql2gEMlXzTTipQre8BBtZMyIvegHamYufBZqIO526JxLeCcv77z-0QtCN7uI5KaquJnRt4ijr9Tm72RNDXd_YmlEYVDhTd3brg-KZ8Dtq4BrMKvpru_omqDZeXKZMU4yK5tmAiJ-9VXYbcmMhsqRzJGfIpO-37-pFnYE34J6EGqnoIVmcQTdw" } ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": "https://api.topcoder-dev.com/v5/groups" + "url": { + "raw": "https://api.topcoder-dev.com/v5/groups", + "protocol": "https", + "host": [ + "api", + "topcoder-dev", + "com" + ], + "path": [ + "v5", + "groups" + ] + } }, "response": [] } - ] + ], + "protocolProfileBehavior": {} } - ] + ], + "protocolProfileBehavior": {} } \ No newline at end of file diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json index 1e93dca..c1f2ab2 100644 --- a/scripts/db/data/User.json +++ b/scripts/db/data/User.json @@ -5,7 +5,7 @@ "updated": "2020-05-05T10:40:48.900Z", "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", - "handle": "handle_05", + "handle": "tc-Admin", "firstName": "John", "lastName": "Doe" }, diff --git a/src/common/es-helper.js b/src/common/es-helper.js index f158006..d07d2f3 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -34,6 +34,49 @@ const MODEL_TO_RESOURCE = { UsersRole: 'userrole' } +const USER_FILTER_TO_MODEL = { + skill: { + name: 'skill', + isAttribute: false, + model: require('../models/Skill'), + queryField: 'name', + esDocumentQuery: 'skills.skillId.keyword', + values: [] + }, + achievement: { + name: 'achievement', + isAttribute: false, + model: require('../models/Achievement'), + queryField: 'name', + esDocumentQuery: 'achievements.id.keyword', + values: [] + }, + location: { + name: 'location', + isAttribute: true, + attributeName: 'location', + esDocumentQuery: 'attributes.attributeId.keyword', + esDocumentValueQuery: 'attributes.value.keyword', + values: [] + }, + isAvailable: { + name: 'isAvailable', + isAttribute: true, + attributeName: 'isAvailable', + esDocumentQuery: 'attributes.attributeId.keyword', + esDocumentValueQuery: 'attributes.value.keyword', + values: [] + }, + company: { + name: 'company', + isAttribute: true, + attributeName: 'company', + esDocumentQuery: 'attributes.attributeId.keyword', + esDocumentValueQuery: 'attributes.value.keyword', + values: [] + } +} + // resource filter config const RESOURCE_FILTER = { // independent resources @@ -211,6 +254,26 @@ function getResource (modelName) { } } +function getTotalCount (total) { + return typeof total === 'number' ? total : total.value +} + +function parseUserFilter (params) { + const filters = {} + _.forOwn(params, (value, key) => { + key = key.toLowerCase() + if (USER_FILTER_TO_MODEL[key]) { + if (!filters[key]) { + filters[key] = USER_FILTER_TO_MODEL[key] + } + + filters[key].values = value + } + }) + + return filters +} + /** * Parse the query parameters to resource filters for enrich. * @param params the request query parameters @@ -473,6 +536,97 @@ function buildEsQueryFromFilter (filter) { return setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) } +async function resolveUserFilterFromDb (filter, { handle }) { + const DBHelper = require('../models/index').DBHelper + if (filter.isAttribute) { + const esQueryClause = { + bool: { + filter: [], + should: [], + minimum_should_match: 0 + } + } + + let organizationId + + // TODO Use the service method instead of raw query + const orgIdLookupResults = await DBHelper.find(require('../models/ExternalProfile'), [ + `select * from DUser, ExternalProfile Where DUser.handle='${handle}' AND DUser.id = ExternalProfile.userId;` + ]) + + if (orgIdLookupResults.length > 0) { + organizationId = orgIdLookupResults[0].organizationId + } + + // 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 = '${filter.attributeName}'` + ]) + + let attributeId + if (attributeIdLookupResults.length > 0) { + attributeId = attributeIdLookupResults[0].id + } else { + throw new Error(`Attribute ${filter.attributeName} is invalid for the current users organization`) + } + + esQueryClause.bool.filter.push({ + term: { + [filter.esDocumentQuery]: attributeId + } + }) + + if (typeof filter.values === 'object') { + for (const value of filter.values) { + esQueryClause.bool.should.push({ + term: { + [filter.esDocumentValueQuery]: value + } + }) + } + } else { + esQueryClause.bool.should.push({ + term: { + [filter.esDocumentValueQuery]: filter.values + } + }) + } + esQueryClause.bool.minimum_should_match = 1 + + return esQueryClause + } else { + const esQueryClause = { + bool: { + should: [], + minimum_should_match: 0 + } + } + + const model = filter.model + // TODO Use the service method instead of raw query + const dbQueries = [ + `${filter.queryField} in (${filter.values.map(f => `'${f}'`).join(',')})` + ] + 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(`User filter ${filter.name} returns no data`) + } + } +} + /** * Resolve filter by querying ES with filter data. * @param filter the filter to query ES @@ -497,10 +651,12 @@ async function resolveResFilter (filter, initialRes) { const esQuery = buildEsQueryFromFilter(filter) const result = await esClient.search(esQuery) - if (result.hits.total > 0) { + const numHits = getTotalCount(result.hits.total) + + if (numHits > 0) { // this value can be array let value - if (result.hits.total === 1) { + if (numHits === 1) { value = result.hits.hits[0]._source.id } else { value = result.hits.hits.map(hit => hit._source.id) @@ -595,6 +751,16 @@ async function searchElasticSearch (resource, ...args) { } } + const userFilters = params.enrich && resource === 'user' ? parseUserFilter(params) : [] + const resolvedUserFilters = [] + if (params.enrich && resource === 'user') { + const filterKey = Object.keys(userFilters) + for (const key of filterKey) { + const resolved = await resolveUserFilterFromDb(userFilters[key], authUser) + resolvedUserFilters.push(resolved) + } + } + // construct ES query const esQuery = { index: doc.userField ? userDoc.index : doc.index, @@ -619,6 +785,11 @@ async function searchElasticSearch (resource, ...args) { 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) { @@ -674,8 +845,8 @@ async function searchElasticSearch (resource, ...args) { logger.debug(`ES query for search ${resource}: ${JSON.stringify(esQuery, null, 2)}`) const docs = await esClient.search(esQuery) - if (docs.hits && docs.hits.total === 0) { - throw new Error('No data returns from ES query') + if (docs.hits && getTotalCount(docs.hits.total) === 0) { + return { total: docs.hits.total, page: params.page, perPage: params.perPage, result: [] } } let result = [] From c93484df4e0ffbb7866bda8df4b7e89882556f3d Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 7 Jun 2020 00:11:18 +0530 Subject: [PATCH 19/93] Fix issue where filter using skills or achievements for enriched users would throw error --- src/common/es-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index d07d2f3..5b7ec63 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -59,7 +59,7 @@ const USER_FILTER_TO_MODEL = { esDocumentValueQuery: 'attributes.value.keyword', values: [] }, - isAvailable: { + isavailable: { name: 'isAvailable', isAttribute: true, attributeName: 'isAvailable', @@ -267,7 +267,7 @@ function parseUserFilter (params) { filters[key] = USER_FILTER_TO_MODEL[key] } - filters[key].values = value + filters[key].values = _.isString(value) ? [value] : value } }) From 3a9a6e81271116f82cbc74b16147308f48363ca6 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 7 Jun 2020 12:29:58 +0530 Subject: [PATCH 20/93] Support sorting of data by location, name and availability --- scripts/constants.js | 3 +- scripts/db/genData.js | 16 ++++++ src/common/es-helper.js | 122 +++++++++++++++++++++++++++++++--------- 3 files changed, 112 insertions(+), 29 deletions(-) diff --git a/scripts/constants.js b/scripts/constants.js index 1dbfcbe..8ac3d04 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -51,7 +51,8 @@ const userResources = { }, userattribute: { propertyName: config.get('ES.DOCUMENTS.userattribute.userField'), - relateKey: 'attributeId' + relateKey: 'attributeId', + nested: true }, userrole: { propertyName: config.get('ES.DOCUMENTS.userrole.userField'), diff --git a/scripts/db/genData.js b/scripts/db/genData.js index cae86ea..fea3401 100644 --- a/scripts/db/genData.js +++ b/scripts/db/genData.js @@ -32,6 +32,22 @@ async function insertIntoES (modelName, body) { 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]) { diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 5b7ec63..e0db352 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -55,24 +55,30 @@ const USER_FILTER_TO_MODEL = { name: 'location', isAttribute: true, attributeName: 'location', + esDocumentPath: 'attributes', esDocumentQuery: 'attributes.attributeId.keyword', esDocumentValueQuery: 'attributes.value.keyword', + defaultSortOrder: 'asc', values: [] }, isavailable: { name: 'isAvailable', isAttribute: true, attributeName: 'isAvailable', + esDocumentPath: 'attributes', esDocumentQuery: 'attributes.attributeId.keyword', esDocumentValueQuery: 'attributes.value.keyword', + defaultSortOrder: 'desc', // results in ordering: true false values: [] }, company: { name: 'company', isAttribute: true, attributeName: 'company', + esDocumentPath: 'attributes', esDocumentQuery: 'attributes.attributeId.keyword', esDocumentValueQuery: 'attributes.value.keyword', + defaultSortOrder: 'asc', values: [] } } @@ -258,13 +264,48 @@ function getTotalCount (total) { return typeof total === 'number' ? total : total.value } +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) { + 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] = USER_FILTER_TO_MODEL[key] + filters[key] = _.clone(USER_FILTER_TO_MODEL[key]) } filters[key].values = _.isString(value) ? [value] : value @@ -536,8 +577,9 @@ function buildEsQueryFromFilter (filter) { return setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) } -async function resolveUserFilterFromDb (filter, { handle }) { +async function resolveUserFilterFromDb (filter, { handle }, organizationId) { const DBHelper = require('../models/index').DBHelper + if (filter.isAttribute) { const esQueryClause = { bool: { @@ -547,31 +589,11 @@ async function resolveUserFilterFromDb (filter, { handle }) { } } - let organizationId - - // TODO Use the service method instead of raw query - const orgIdLookupResults = await DBHelper.find(require('../models/ExternalProfile'), [ - `select * from DUser, ExternalProfile Where DUser.handle='${handle}' AND DUser.id = ExternalProfile.userId;` - ]) - - if (orgIdLookupResults.length > 0) { - organizationId = orgIdLookupResults[0].organizationId + if (organizationId == null) { + organizationId = await getOrganizationId(handle) } - // 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 = '${filter.attributeName}'` - ]) - - let attributeId - if (attributeIdLookupResults.length > 0) { - attributeId = attributeIdLookupResults[0].id - } else { - throw new Error(`Attribute ${filter.attributeName} is invalid for the current users organization`) - } + const attributeId = await getAttributeId(organizationId, filter.attributeName) esQueryClause.bool.filter.push({ term: { @@ -596,7 +618,12 @@ async function resolveUserFilterFromDb (filter, { handle }) { } esQueryClause.bool.minimum_should_match = 1 - return esQueryClause + return { + nested: { + path: filter.esDocumentPath, + query: esQueryClause + } + } } else { const esQueryClause = { bool: { @@ -627,6 +654,37 @@ async function resolveUserFilterFromDb (filter, { handle }) { } } +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 @@ -725,6 +783,8 @@ async function searchElasticSearch (resource, ...args) { params.perPage = config.PAGE_SIZE } + let sortClause = [] + const preResFilters = parseResourceFilter(resource, params, false) const preResFilterResults = [] // resolve pre resource filters @@ -755,10 +815,15 @@ async function searchElasticSearch (resource, ...args) { const resolvedUserFilters = [] if (params.enrich && resource === 'user') { const filterKey = Object.keys(userFilters) + let authUserOrganizationId // Fetch and hold organizationId so subsequent filter resolution needn't make the same DB query to fetch it again for (const key of filterKey) { - const resolved = await resolveUserFilterFromDb(userFilters[key], authUser) + 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 @@ -774,7 +839,8 @@ async function searchElasticSearch (resource, ...args) { should: [], minimum_should_match: 0 } - } + }, + sort: sortClause } } From e783309d2e6421f82ee1d7f16019bf6a89f3e5f4 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 7 Jun 2020 17:04:50 +0530 Subject: [PATCH 21/93] Update dummy data for filtering skills and achievements --- config/default.js | 2 +- scripts/db/data/Achievement.json | 19 ++++++++++-- scripts/db/data/Skill.json | 37 +++++++++++++++++++++-- scripts/db/data/UsersSkill.json | 50 +++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 7 deletions(-) diff --git a/config/default.js b/config/default.js index 7cd79a9..46be717 100755 --- a/config/default.js +++ b/config/default.js @@ -39,7 +39,7 @@ module.exports = { // ElasticSearch ES: { HOST: process.env.ES_HOST || 'localhost:9200', - API_VERSION: process.env.ES_API_VERSION || '6.8', + API_VERSION: process.env.ES_API_VERSION || '7.4', // es mapping: _index, _type, _id DOCUMENTS: { achievementprovider: { diff --git a/scripts/db/data/Achievement.json b/scripts/db/data/Achievement.json index 6e6ea57..538433b 100644 --- a/scripts/db/data/Achievement.json +++ b/scripts/db/data/Achievement.json @@ -6,7 +6,7 @@ "createdBy": "tc-Admin", "updatedBy": "tc-Admin", "achievementsProviderId": "eb327c00-0090-45af-96d2-593408c96397", - "name": "string", + "name": "Informatika", "uri": "string", "certifierId": "string", "certifiedDate": "2020-05-13T06:33:54.708Z", @@ -19,10 +19,23 @@ "createdBy": "tc-user", "updatedBy": null, "achievementsProviderId": "ce05133f-129e-484d-9ef9-72bf51ff81f9", - "name": "achievement-name-01", + "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": "Upwork", + "uri": "http://www.google.com/xx", + "certifierId": "certifierId", + "certifiedDate": "2020-05-04T07:36:28.036Z", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" } -] \ No newline at end of file +] diff --git a/scripts/db/data/Skill.json b/scripts/db/data/Skill.json index 75e8860..31a88b6 100644 --- a/scripts/db/data/Skill.json +++ b/scripts/db/data/Skill.json @@ -6,8 +6,41 @@ "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", - "name": "skill_name_update", + "name": ".Net", + "externalId": "externalId", + "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++", + "externalId": "externalId", + "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": "Rust", + "externalId": "externalId", + "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": "api", "externalId": "externalId", "uri": "http://www.google.com" } -] \ No newline at end of file +] diff --git a/scripts/db/data/UsersSkill.json b/scripts/db/data/UsersSkill.json index e8c4623..f7e4c18 100644 --- a/scripts/db/data/UsersSkill.json +++ b/scripts/db/data/UsersSkill.json @@ -10,5 +10,53 @@ "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" } -] \ No newline at end of file +] From 36859ec0e2b811538a36da1df4fbea99c67a5832 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 7 Jun 2020 17:50:43 +0530 Subject: [PATCH 22/93] Update swagger specification for the new query params --- docs/swagger.yaml | 86 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index e93fb3c..3c1e215 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -57,23 +57,50 @@ paths: description: "Filter by user handle" required: false type: "string" - - name: "groupId" + - name: "enrich" in: "query" - description: "Filter by user group Id" + 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" - format: "UUID" + description: "Sort order" + enum: ["name", "location", "isAvailable"] - name: "roleId" in: "query" description: "Filter by user roleId" required: false type: "string" format: "UUID" - - name: "enrich" - in: "query" - description: "Enrich all child resources under the user" - required: false - type: "boolean" - name: "userSkill.skillId" in: "query" required: false @@ -219,23 +246,50 @@ paths: description: "Filter by user handle" required: false type: "string" - - name: "groupId" + - name: "enrich" in: "query" - description: "Filter by user group Id" + 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" - format: "UUID" + description: "Sort order" + enum: ["name", "location", "isAvailable"] - name: "roleId" in: "query" description: "Filter by user roleId" required: false type: "string" format: "UUID" - - name: "enrich" - in: "query" - description: "Enrich all child resources under the user" - required: false - type: "boolean" - name: "userSkill.skillId" in: "query" required: false From 9cc73ea455abd8dc6c81115de450a6bb18e86f71 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 7 Jun 2020 22:53:45 +0530 Subject: [PATCH 23/93] Fix pagination header value --- src/common/helper.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index a4609f8..b3523fe 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -181,7 +181,9 @@ function injectSearchMeta (req, res, result) { return } - const totalPages = Math.ceil(result.total / result.perPage) + 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) } @@ -190,7 +192,7 @@ function injectSearchMeta (req, res, result) { } res.set('X-Page', result.page) res.set('X-Per-Page', result.perPage) - res.set('X-Total', result.total) + res.set('X-Total', resultTotal) res.set('X-Total-Pages', totalPages) // set Link header if (totalPages > 0) { From 83346a6d4f465c50d940ae9f1015d74bba560ee9 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 9 Jun 2020 11:18:22 +0530 Subject: [PATCH 24/93] Improve error message --- src/common/es-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index e0db352..70b234f 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -649,7 +649,7 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { esQueryClause.bool.minimum_should_match = 1 return esQueryClause } else { - throw new Error(`User filter ${filter.name} returns no data`) + 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`) } } } From 006ec0f447992f31111ec5968aca89c7a567ebb4 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 9 Jun 2020 11:26:56 +0530 Subject: [PATCH 25/93] Fix issue where deleting junction table record would not publish the details to bus api correctly --- src/common/es-helper.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 70b234f..2814c4b 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1008,8 +1008,12 @@ function wrapElasticSearchOp (methods, Model) { let result = await func(...args) // remove action returns undefined, pass id to elasticsearch if (func.name === 'remove') { - result = { - id: args[0] + if (SUB_DOCUMENTS[resource]) { + result = _.assign({}, args[2]) + } else { + result = { + id: args[0] + } } } try { From e11d2e97f49fd79021ca04297fb624309e80afa3 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 9 Jun 2020 16:38:27 +0530 Subject: [PATCH 26/93] Support querying skills using externalId and skillProviderId --- docs/swagger.yaml | 27 ++++++++++++++++++++++++++- scripts/db/data/Skill.json | 16 ++++++++-------- scripts/db/data/SkillsProvider.json | 12 ++---------- src/common/es-helper.js | 10 ++++++++++ src/modules/skill/service.js | 13 ++++++++++++- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 3c1e215..961d29f 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -865,6 +865,15 @@ paths: 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" @@ -890,7 +899,18 @@ paths: \ 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: [] + 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" @@ -3790,6 +3810,11 @@ definitions: 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" diff --git a/scripts/db/data/Skill.json b/scripts/db/data/Skill.json index 31a88b6..a0e8da1 100644 --- a/scripts/db/data/Skill.json +++ b/scripts/db/data/Skill.json @@ -6,8 +6,8 @@ "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", - "name": ".Net", - "externalId": "externalId", + "name": ".NET Framework 4", + "externalId": "KS126XR63RKYVCKYDNBN", "uri": "http://www.google.com" }, { @@ -17,8 +17,8 @@ "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", - "name": "C++", - "externalId": "externalId", + "name": "C++ (Programming Language)", + "externalId": "KS1219W70LY1GXZDSKW5", "uri": "http://www.google.com" }, { @@ -28,8 +28,8 @@ "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", - "name": "Rust", - "externalId": "externalId", + "name": "Angular (Web Framework)", + "externalId": "KS120H6772VQ0MQ5RLVD", "uri": "http://www.google.com" }, { @@ -39,8 +39,8 @@ "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", "skillProviderId": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", - "name": "api", - "externalId": "externalId", + "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 index 1e64a84..b567a3c 100644 --- a/scripts/db/data/SkillsProvider.json +++ b/scripts/db/data/SkillsProvider.json @@ -5,14 +5,6 @@ "updated": null, "createdBy": "tc-Copilot", "updatedBy": null, - "name": "skillsProviders_01" - }, - { - "id": "77003860-3c24-4438-84e7-85ab8a4358d6", - "created": "2020-05-05T11:02:32.932Z", - "updated": null, - "createdBy": "tc-Copilot", - "updatedBy": null, - "name": "skillsProviders_01" + "name": "EMSI" } -] \ No newline at end of file +] diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 2814c4b..32ef539 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -107,6 +107,16 @@ const RESOURCE_FILTER = { queryField: 'name' } }, + skill: { + externalId: { + resource: 'skill', + queryField: 'externalId' + }, + skillProviderId: { + resource: 'skill', + queryField: 'skillProviderId' + } + }, skillprovider: { name: { resource: 'skillprovider', diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js index c63f278..35010d1 100644 --- a/src/modules/skill/service.js +++ b/src/modules/skill/service.js @@ -20,7 +20,18 @@ const methods = helper.getServiceMethods( externalId: joi.string() }, {}, - async () => []) + async query => { + const dbQueries = [] + if (query.externalId) { + dbQueries.push(`externalId like '%${query.externalId}%'`) + } + if (query.skillProviderId) { + dbQueries.push(`skillProviderId like '%${query.skillProviderId}%'`) + } + return dbQueries + }, + [['skillProviderId', 'externalId']] +) module.exports = { ...methods From 7228492679e1509e0025651211301cc30aefac04 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 10 Jun 2020 12:43:22 +0530 Subject: [PATCH 27/93] Add tonyj user for org mapping --- scripts/db/data/ExternalProfile.json | 12 +++++++++++- scripts/db/data/User.json | 10 ++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/db/data/ExternalProfile.json b/scripts/db/data/ExternalProfile.json index 1aaee50..95118f9 100644 --- a/scripts/db/data/ExternalProfile.json +++ b/scripts/db/data/ExternalProfile.json @@ -8,5 +8,15 @@ "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf", "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", "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", + "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "uri": "http://www.new.com/new-uri" } -] \ No newline at end of file +] diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json index c1f2ab2..6dc831f 100644 --- a/scripts/db/data/User.json +++ b/scripts/db/data/User.json @@ -18,5 +18,15 @@ "handle": "handle_01", "firstName": "Mark", "lastName": "Robinson" + }, + { + "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" } ] From 1ff41d79fd6658c2b3c0b42bab995cb04c3f410c Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 10 Jun 2020 14:07:15 +0530 Subject: [PATCH 28/93] Create endpoints to search for users and to search for attribute values --- src/common/es-helper.js | 15 ++++++++++++++- src/common/helper.js | 2 +- src/modules/search/controller.js | 27 +++++++++++++++++++++++++++ src/modules/search/route.js | 25 +++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/modules/search/controller.js create mode 100644 src/modules/search/route.js diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 32ef539..c13ea01 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1037,6 +1037,19 @@ function wrapElasticSearchOp (methods, Model) { }) } +async function searchUsers (filter) { + // Placeholder for searching users + return { total: 0, result: [] } +} + +async function searchAttributeValues ({ attributeId, attributeValue }) { + // Placeholder for searching for attribute values + // Return maximum 5 values at a time + return { total: 0, result: [] } +} + module.exports = { - wrapElasticSearchOp + wrapElasticSearchOp, + searchUsers, + searchAttributeValues } diff --git a/src/common/helper.js b/src/common/helper.js index b3523fe..a5fe2e3 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -181,7 +181,7 @@ function injectSearchMeta (req, res, result) { return } - const resultTotal = _.isNumber(result.total) ? result.total : result.total.value; + const resultTotal = _.isNumber(result.total) ? result.total : result.total.value const totalPages = Math.ceil(resultTotal / result.perPage) if (result.page > 1) { diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js new file mode 100644 index 0000000..71cfefb --- /dev/null +++ b/src/modules/search/controller.js @@ -0,0 +1,27 @@ +/** + * The search controller + */ +const esHelper = require('../../common/es-helper') +const { injectSearchMeta } = require('../../common/helper') + +/** + * Search for users. Returns enriched users + */ +async function searchUsers (req, res) { + const result = await esHelper.searchUsers(req.body) + injectSearchMeta(req, res, result) + res.send(result.result) +} + +/** + * Search for attribute values + */ +async function searchAttributeValues (req, res) { + const result = await esHelper.searchAttributeValues(req.query) + res.send(result.result) +} + +module.exports = { + searchUsers, + searchAttributeValues +} diff --git a/src/modules/search/route.js b/src/modules/search/route.js new file mode 100644 index 0000000..69d5c3e --- /dev/null +++ b/src/modules/search/route.js @@ -0,0 +1,25 @@ +/** + * the search routes + */ + +const Controller = require('./controller') +const consts = require('../../consts') + +module.exports = { + '/search/users': { + post: { + method: Controller.searchUsers, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['read:user', 'all:user'] + } + }, + '/search/userAttributes': { + get: { + method: Controller.searchAttributeValues, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['create:userAttribute', 'all:userAttribute'] + } + } +} From 7e6257112965f11628376015880aef66b0b2607f Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 10 Jun 2020 14:42:09 +0530 Subject: [PATCH 29/93] Correct the name used --- scripts/db/data/User.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json index 6dc831f..0a51c59 100644 --- a/scripts/db/data/User.json +++ b/scripts/db/data/User.json @@ -25,7 +25,7 @@ "updated": null, "createdBy": "tc-Copilot", "updatedBy": null, - "handle": "Tonyj", + "handle": "TonyJ", "firstName": "Tony", "lastName": "J" } From 5f0573c40be2a8a2c72f44b11262af096a5f2a98 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 10 Jun 2020 22:53:41 +0530 Subject: [PATCH 30/93] New attribute externalId on external profile model --- scripts/db/data/ExternalProfile.json | 2 ++ scripts/db/data/Organization.json | 4 ++-- src/models/ExternalProfile.js | 1 + src/modules/externalProfile/service.js | 7 +++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/scripts/db/data/ExternalProfile.json b/scripts/db/data/ExternalProfile.json index 95118f9..991600e 100644 --- a/scripts/db/data/ExternalProfile.json +++ b/scripts/db/data/ExternalProfile.json @@ -7,6 +7,7 @@ "updatedBy": "tc-Admin", "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf", "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "1234567", "uri": "http://www.new.com/new-uri" }, { @@ -17,6 +18,7 @@ "updatedBy": "tc-Admin", "userId": "3f64739e-10bf-42ca-8314-8aea0245cd0f", "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8547899", "uri": "http://www.new.com/new-uri" } ] diff --git a/scripts/db/data/Organization.json b/scripts/db/data/Organization.json index 5811c91..6859578 100644 --- a/scripts/db/data/Organization.json +++ b/scripts/db/data/Organization.json @@ -5,6 +5,6 @@ "updated": "2020-05-05T11:02:10.574Z", "createdBy": "tc-Copilot", "updatedBy": "tc-Copilot", - "name": "organization_update" + "name": "Topcoder" } -] \ No newline at end of file +] diff --git a/src/models/ExternalProfile.js b/src/models/ExternalProfile.js index f90fe2b..5b8d720 100644 --- a/src/models/ExternalProfile.js +++ b/src/models/ExternalProfile.js @@ -8,6 +8,7 @@ class ExternalProfile extends RecordObject { super() this.userId = null this.organizationId = null + this.externalId = null this.uri = null } } diff --git a/src/modules/externalProfile/service.js b/src/modules/externalProfile/service.js index 8d3cc6d..e36f270 100644 --- a/src/modules/externalProfile/service.js +++ b/src/modules/externalProfile/service.js @@ -10,15 +10,18 @@ const methods = helper.getServiceMethods( { // create request body joi schema userId: joi.string().required(), organizationId: joi.string().required(), + externalId: joi.string().required(), uri: joi.string().required() }, { // patch request body joi schema userId: joi.string().required(), organizationId: joi.string().required(), + externalId: joi.string(), uri: joi.string() }, { // search request query joi schema userId: joi.string().required(), + externalId: joi.string(), organizationName: joi.string() }, async (query) => { // build search query by request @@ -29,6 +32,10 @@ const methods = helper.getServiceMethods( if (query.organizationName) { dbQueries.push(`Organization.name like '%${query.organizationName}%'`) } + if (query.externalId) { + dbQueries.push(`Organization.externalId like '%${query.externalId}%'`) + } + return dbQueries }, [['userId', 'organizationId']] // unique fields From 1830dbe9a20e5ad31d7c714ed67b965e7b81559e Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 11 Jun 2020 12:57:27 +0530 Subject: [PATCH 31/93] New endpoints - /search/users and /search/userAttributes --- docs/UBahn_API.postman_collection.json | 137 ++++++++++++++-- scripts/db/data/Achievement.json | 2 +- src/common/es-helper.js | 216 +++++++++++++++++++++++-- src/modules/search/controller.js | 2 +- 4 files changed, 326 insertions(+), 31 deletions(-) diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index 45ba8a3..48ed820 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -1,10 +1,119 @@ { "info": { - "_postman_id": "8248607f-018f-4ac2-b088-edb49f421ff0", + "_postman_id": "5c139c1e-e3f2-4002-a278-e03c7b7dc97d", "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": "ba04f45f-dbdf-4398-9750-3eae3161dedf", + "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/users", + "event": [ + { + "listen": "test", + "script": { + "id": "64aeda82-1df8-41c7-80c9-d5cf8423fdf9", + "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": [ @@ -14,7 +123,7 @@ { "listen": "test", "script": { - "id": "b469546c-8293-4bb3-afb2-45e14b093bc8", + "id": "e4f1d08d-a9b5-4efe-b3d9-67d3111608c1", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -372,7 +481,7 @@ { "listen": "test", "script": { - "id": "d8f3f5d1-c62b-4dde-ae13-a6c644917400", + "id": "816c38da-3158-4d9d-828e-bb0644178a98", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"roleId\", rsp.id);" @@ -607,7 +716,7 @@ { "listen": "test", "script": { - "id": "4a4fc48b-a080-4ba1-985f-233099d955ca", + "id": "7671acab-f1f8-4c29-9824-f1f2bd570028", "exec": [ "" ], @@ -849,7 +958,7 @@ { "listen": "test", "script": { - "id": "b4080e98-c6a7-452a-9f42-2b0d78b606d7", + "id": "5b67a6ba-63b6-4991-a5ea-d881ea802083", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"organizationId\", rsp.id);" @@ -1084,7 +1193,7 @@ { "listen": "test", "script": { - "id": "d5d06aca-cc7c-4a0c-a602-44b0768bf509", + "id": "88d7b18f-c5e1-4577-9d03-a93d9f9d5c7e", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillsProviderId\", rsp.id);" @@ -1319,7 +1428,7 @@ { "listen": "test", "script": { - "id": "79033f51-2602-4303-bd92-898814d4b6fd", + "id": "92b83849-961d-4ad3-bac2-d7f457e5fbd7", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillId\", rsp.id);" @@ -1555,7 +1664,7 @@ { "listen": "test", "script": { - "id": "c864cd20-ea0c-40c6-9c6b-f50dbbea2cfb", + "id": "d5c59be3-63df-485b-a889-aa4d00c90571", "exec": [ "" ], @@ -1803,7 +1912,7 @@ { "listen": "test", "script": { - "id": "a61bd92d-8189-4005-ae58-d28b4b475aca", + "id": "d8143ed1-b600-4ef3-845c-396ed909e821", "exec": [ "" ], @@ -2051,7 +2160,7 @@ { "listen": "test", "script": { - "id": "0cd87552-e2d3-4510-8ad3-a2bd411d06e2", + "id": "6c13436d-c2ee-4867-98ac-eb513e9edb3e", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"achievementsProviderId\", rsp.id);" @@ -2286,7 +2395,7 @@ { "listen": "test", "script": { - "id": "0183c828-5145-468a-b2ce-29b39722f573", + "id": "4453dd7e-65fc-47c0-9081-7a78a6ed1446", "exec": [ "" ], @@ -2535,7 +2644,7 @@ { "listen": "test", "script": { - "id": "5117cfff-e15d-4ce9-958b-6f0ece556de2", + "id": "d34d4953-a1f9-41ef-a9dd-6fa7c9431c3f", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeGroupId\", rsp.id);" @@ -2776,7 +2885,7 @@ { "listen": "test", "script": { - "id": "fff8a571-407c-42e9-b7b6-fe50896b0d92", + "id": "71539226-1b42-4aa3-9f51-d5b488c817d2", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeId\", rsp.id);" @@ -3016,7 +3125,7 @@ { "listen": "test", "script": { - "id": "9f864ea3-c055-4ff1-97b8-1c80094d3710", + "id": "5c987711-36d9-4472-b6d0-db4edc47685e", "exec": [ "" ], diff --git a/scripts/db/data/Achievement.json b/scripts/db/data/Achievement.json index 538433b..d7f37e5 100644 --- a/scripts/db/data/Achievement.json +++ b/scripts/db/data/Achievement.json @@ -32,7 +32,7 @@ "createdBy": "tc-user", "updatedBy": null, "achievementsProviderId": "ce05133f-129e-484d-9ef9-72bf51ff81f9", - "name": "Upwork", + "name": "Topcoder", "uri": "http://www.google.com/xx", "certifierId": "certifierId", "certifiedDate": "2020-05-04T07:36:28.036Z", diff --git a/src/common/es-helper.js b/src/common/es-helper.js index c13ea01..2c48f90 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -34,6 +34,13 @@ const MODEL_TO_RESOURCE = { UsersRole: 'userrole' } +const USER_ATTRIBUTE = { + esDocumentPath: 'attributes', + esDocumentQuery: 'attributes.attributeId.keyword', + esDocumentValueStringQuery: 'attributes.value', + esDocumentValueQuery: 'attributes.value.keyword' +} + const USER_FILTER_TO_MODEL = { skill: { name: 'skill', @@ -43,6 +50,7 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'skills.skillId.keyword', values: [] }, + get skills () { return this.skill }, achievement: { name: 'achievement', isAttribute: false, @@ -51,23 +59,25 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'achievements.id.keyword', values: [] }, + get achievements () { return this.achievement }, location: { name: 'location', isAttribute: true, attributeName: 'location', - esDocumentPath: 'attributes', - esDocumentQuery: 'attributes.attributeId.keyword', - esDocumentValueQuery: 'attributes.value.keyword', + 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: 'attributes', - esDocumentQuery: 'attributes.attributeId.keyword', - esDocumentValueQuery: 'attributes.value.keyword', + esDocumentPath: USER_ATTRIBUTE.esDocumentPath, + esDocumentQuery: USER_ATTRIBUTE.esDocumentQuery, + esDocumentValueQuery: USER_ATTRIBUTE.esDocumentValueQuery, defaultSortOrder: 'desc', // results in ordering: true false values: [] }, @@ -75,9 +85,9 @@ const USER_FILTER_TO_MODEL = { name: 'company', isAttribute: true, attributeName: 'company', - esDocumentPath: 'attributes', - esDocumentQuery: 'attributes.attributeId.keyword', - esDocumentValueQuery: 'attributes.value.keyword', + esDocumentPath: USER_ATTRIBUTE.esDocumentPath, + esDocumentQuery: USER_ATTRIBUTE.esDocumentQuery, + esDocumentValueQuery: USER_ATTRIBUTE.esDocumentValueQuery, defaultSortOrder: 'asc', values: [] } @@ -562,6 +572,43 @@ function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) 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 => ({ + term: { + [USER_ATTRIBUTE.esDocumentValueQuery]: val + } + })), + minimum_should_match: 1 + } + } + } + }) + } +} + /** * Build ES query from given filter. * @param filter @@ -587,6 +634,72 @@ function buildEsQueryFromFilter (filter) { return setFilterValueToEsQuery(esQuery, matchField, filter.value, filter.queryField) } +/** + * 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 page result page, defaults to 1 + * @param perPage maximum number of matches to return, defaults to 5 + * @return {{}} created ES query object + */ +function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1, perPage = 5) { + const queryDoc = DOCUMENTS.user + + const matchConditions = [{ + match: { + [USER_ATTRIBUTE.esDocumentQuery]: attributeId + } + }] + + if (attributeValue != null) { + matchConditions.push({ + query_string: { + default_field: USER_ATTRIBUTE.esDocumentValueStringQuery, + // minimum_should_match: `100%`, /* disallow misspellings */ + query: `*${attributeValue}*` + } + }) + } + + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + query: { + nested: { + path: USER_ATTRIBUTE.esDocumentPath, + query: { + bool: { + must: matchConditions + } + }, + inner_hits: {} // Get the attriute values + } + }, + sort: [{ + [USER_ATTRIBUTE.esDocumentValueQuery]: { + order: 'asc', + nested: { + path: USER_ATTRIBUTE.esDocumentPath, + filter: { + term: { + [USER_ATTRIBUTE.esDocumentQuery]: attributeId + } + } + } + } + }], + size: perPage, + from: (page - 1) * perPage, + _source: { + excludes: '*' // Parent document matches aren't required + } + } + } + + return esQuery +} + async function resolveUserFilterFromDb (filter, { handle }, organizationId) { const DBHelper = require('../models/index').DBHelper @@ -1037,15 +1150,88 @@ function wrapElasticSearchOp (methods, Model) { }) } -async function searchUsers (filter) { - // Placeholder for searching users - return { total: 0, result: [] } +async function searchUsers (authUser, filter, params) { + const { checkIfExists, getAuthUser } = require('./helper') + const queryDoc = DOCUMENTS.user + + if (!params.page) { + params.page = 1 + } + if (!params.perPage) { + params.perPage = config.PAGE_SIZE + } + + let sortClause = [] + + const userFilters = parseUserFilter(filter) + const resolvedUserFilters = [] + + let authUserOrganizationId + 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.UserRoles.admin, appConst.UserRoles.administrator])) { + setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') + } + + if (resolvedUserFilters.length > 0) { + esQuery.body.query.bool.filter = resolvedUserFilters + } + + if (filter.attributes != null) { + setUserAttributesFiltersToEsQuery(esQuery.body.query.bool.filter, filter.attributes) + } + + 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) + const result = await enrichUsers(users) + // enrich groups + for (const user of users) { + const groups = await groupApi.getGroups('user', user.id) + user.groups = groups + } + + return { total: result.length, page: params.page, perPage: params.perPage, result } } async function searchAttributeValues ({ attributeId, attributeValue }) { - // Placeholder for searching for attribute values - // Return maximum 5 values at a time - return { total: 0, result: [] } + const esQuery = buildEsQueryToGetAttributeValues(attributeId, attributeValue) + logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) + + const esResult = await esClient.search(esQuery) + const result = esResult.hits.hits.map(hit => { + return hit.inner_hits.attributes.hits.hits[0]._source + }) + + return { total: result.length, result: result } } module.exports = { diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js index 71cfefb..421ca23 100644 --- a/src/modules/search/controller.js +++ b/src/modules/search/controller.js @@ -8,7 +8,7 @@ const { injectSearchMeta } = require('../../common/helper') * Search for users. Returns enriched users */ async function searchUsers (req, res) { - const result = await esHelper.searchUsers(req.body) + const result = await esHelper.searchUsers(req.auth, req.body, req.query) injectSearchMeta(req, res, result) res.send(result.result) } From 74ff310e9b8116591cb7d55fd363f9b5adaf8a0e Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 11 Jun 2020 20:13:22 +0530 Subject: [PATCH 32/93] Fix issue where searching external profiles by external id not working --- src/modules/externalProfile/service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/externalProfile/service.js b/src/modules/externalProfile/service.js index e36f270..8cfcfe1 100644 --- a/src/modules/externalProfile/service.js +++ b/src/modules/externalProfile/service.js @@ -33,7 +33,7 @@ const methods = helper.getServiceMethods( dbQueries.push(`Organization.name like '%${query.organizationName}%'`) } if (query.externalId) { - dbQueries.push(`Organization.externalId like '%${query.externalId}%'`) + dbQueries.push(`ExternalProfile.externalId like '%${query.externalId}%'`) } return dbQueries From e754e048e67f4d92c8a93fb77e20d9abcb4d4f05 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 11 Jun 2020 20:36:48 +0530 Subject: [PATCH 33/93] Setup data for company attributes --- scripts/db/data/Attribute.json | 36 +++++++ scripts/db/data/UserAttribute.json | 160 +++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/scripts/db/data/Attribute.json b/scripts/db/data/Attribute.json index b1b835b..14b41b8 100644 --- a/scripts/db/data/Attribute.json +++ b/scripts/db/data/Attribute.json @@ -34,5 +34,41 @@ "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/UserAttribute.json b/scripts/db/data/UserAttribute.json index 4a74230..a0cd1e7 100644 --- a/scripts/db/data/UserAttribute.json +++ b/scripts/db/data/UserAttribute.json @@ -39,6 +39,46 @@ "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": "20721207-c868-4ee4-8ac4-59db683b7bce", "created": "2020-05-13T08:48:30.541Z", @@ -78,5 +118,125 @@ "attributeId": "c44d4bee-1356-46d6-9f1f-991936dec297", "value": "IT Support Specialist", "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "74314457", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "Female", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "3", + "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" + }, + { + "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": "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" } ] From 07ae64b4693bb9da72faeb16ee559fc2c0c5dfa3 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sat, 13 Jun 2020 18:37:09 +0530 Subject: [PATCH 34/93] Update query params passed to endpoint to get groups for user --- src/common/es-helper.js | 6 +++--- src/common/group-api.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 2c48f90..0237271 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -466,7 +466,7 @@ async function getFromElasticSearch (resource, ...args) { if (params.enrich && resource === 'user') { const user = await enrichUser(result) - const groups = await groupApi.getGroups('user', user.id) + const groups = await groupApi.getGroups(user.id) user.groups = groups return user } else if (subDoc) { @@ -1044,7 +1044,7 @@ async function searchElasticSearch (resource, ...args) { result = await enrichUsers(users) // enrich groups for (const user of users) { - const groups = await groupApi.getGroups('user', user.id) + const groups = await groupApi.getGroups(user.id) user.groups = groups } } else if (topSubDoc) { @@ -1215,7 +1215,7 @@ async function searchUsers (authUser, filter, params) { const result = await enrichUsers(users) // enrich groups for (const user of users) { - const groups = await groupApi.getGroups('user', user.id) + const groups = await groupApi.getGroups(user.id) user.groups = groups } diff --git a/src/common/group-api.js b/src/common/group-api.js index da4b492..4ce6ff6 100644 --- a/src/common/group-api.js +++ b/src/common/group-api.js @@ -12,11 +12,11 @@ async function getM2Mtoken () { return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) } -async function getGroups (membershipType, memberId) { +async function getGroups (universalUID) { const m2mToken = await getM2Mtoken() const resp = await axios({ method: 'get', - params: { membershipType, memberId }, + params: { universalUID }, url: config.GROUP_API_URL, headers: { Authorization: `Bearer ${m2mToken}` } }) From 7faabda66fdaac96bcbdb68f33ea3a6703274830 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sat, 13 Jun 2020 18:45:23 +0530 Subject: [PATCH 35/93] Return empty groups if group api fails --- src/common/group-api.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/common/group-api.js b/src/common/group-api.js index 4ce6ff6..cd5bfbb 100644 --- a/src/common/group-api.js +++ b/src/common/group-api.js @@ -2,6 +2,8 @@ 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'])) /** @@ -14,13 +16,19 @@ async function getM2Mtoken () { async function getGroups (universalUID) { const m2mToken = await getM2Mtoken() - const resp = await axios({ - method: 'get', - params: { universalUID }, - url: config.GROUP_API_URL, - headers: { Authorization: `Bearer ${m2mToken}` } - }) - return resp.data + + try { + const resp = await axios({ + method: 'get', + params: { universalUID }, + url: config.GROUP_API_URL, + headers: { Authorization: `Bearer ${m2mToken}` } + }) + return resp.data + } catch (error) { + logger.error(error) + return [] + } } module.exports = { From c2a2f5b61ee7acf172e41f32a26f69f735b3b02c Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sat, 13 Jun 2020 19:27:40 +0530 Subject: [PATCH 36/93] Fix issue where users groups were not being returned --- src/common/group-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/group-api.js b/src/common/group-api.js index cd5bfbb..9115657 100644 --- a/src/common/group-api.js +++ b/src/common/group-api.js @@ -20,7 +20,7 @@ async function getGroups (universalUID) { try { const resp = await axios({ method: 'get', - params: { universalUID }, + params: { universalUID, membershipType: 'user' }, url: config.GROUP_API_URL, headers: { Authorization: `Bearer ${m2mToken}` } }) From e270d2d8282899fd7c9805d309fcf72059b52d4f Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 14 Jun 2020 21:52:11 +0530 Subject: [PATCH 37/93] Support keyword search --- src/common/es-helper.js | 65 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 0237271..a3e40b3 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -56,6 +56,7 @@ const USER_FILTER_TO_MODEL = { isAttribute: false, model: require('../models/Achievement'), queryField: 'name', + esDocumentValueQuery: 'achievements.name', esDocumentQuery: 'achievements.id.keyword', values: [] }, @@ -609,6 +610,64 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } } +/** + * Get skillIds matching the search keyword + * + * @param keyword the search keyword + * @returns array of skillIds + */ +async function searchSkills (keyword) { + const queryDoc = DOCUMENTS.skill + const esQuery = { + index: queryDoc.index, + type: queryDoc.type, + body: { + query: { + query_string: { + default_field: 'name', + query: `*${keyword}*` + } + }, + _source: 'id' + } + } + + 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.id) +} + +async function setUserSearchClausesToEsQuery (boolClause, keyword) { + const skillIds = await searchSkills(keyword) + + boolClause.should.push({ + nested: { + path: USER_ATTRIBUTE.esDocumentPath, + query: { + query_string: { + query: `*${keyword}*`, + fields: [USER_ATTRIBUTE.esDocumentValueStringQuery] + } + } + } + }, { + query_string: { + query: `*${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 @@ -651,7 +710,7 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1 } }] - if (attributeValue != null) { + if (attributeValue !== null) { matchConditions.push({ query_string: { default_field: USER_ATTRIBUTE.esDocumentValueStringQuery, @@ -1204,6 +1263,10 @@ async function searchUsers (authUser, filter, params) { 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) } From b8ffc277d59cbab5c9d7e39d2db9d2c44b931114 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 15 Jun 2020 19:07:00 +0530 Subject: [PATCH 38/93] Update mock data --- scripts/db/data/ExternalProfile.json | 286 ++++ scripts/db/data/User.json | 237 ++- scripts/db/data/UserAttribute.json | 2000 ++++++++++++++++++++++++++ scripts/db/data/UsersSkill.json | 576 ++++++++ 4 files changed, 3092 insertions(+), 7 deletions(-) diff --git a/scripts/db/data/ExternalProfile.json b/scripts/db/data/ExternalProfile.json index 991600e..df49d2d 100644 --- a/scripts/db/data/ExternalProfile.json +++ b/scripts/db/data/ExternalProfile.json @@ -20,5 +20,291 @@ "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "userId": "bdcb113f-6715-40fd-8dab-14aa01327ae9", + "organizationId": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", + "externalId": "8819322", + "uri": "http://jaden.net" } ] diff --git a/scripts/db/data/User.json b/scripts/db/data/User.json index 0a51c59..c8d2490 100644 --- a/scripts/db/data/User.json +++ b/scripts/db/data/User.json @@ -8,18 +8,16 @@ "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": "handle_01", - "firstName": "Mark", - "lastName": "Robinson" - }, - { + "handle": "lazybaer", + "firstName": "Christopher", + "lastName": "DeLaurentis" + }, { "id": "3f64739e-10bf-42ca-8314-8aea0245cd0f", "created": "2020-05-05T10:18:03.882Z", "updated": null, @@ -28,5 +26,230 @@ "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 index a0cd1e7..c370b5d 100644 --- a/scripts/db/data/UserAttribute.json +++ b/scripts/db/data/UserAttribute.json @@ -238,5 +238,2005 @@ "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/UsersSkill.json b/scripts/db/data/UsersSkill.json index f7e4c18..c0c224e 100644 --- a/scripts/db/data/UsersSkill.json +++ b/scripts/db/data/UsersSkill.json @@ -58,5 +58,581 @@ "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" } ] From bb44d9fad2dfb9146cee3ec9368efc088474290b Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 15 Jun 2020 20:34:19 +0530 Subject: [PATCH 39/93] #12 - Fix issue where invalid model attributes did not raise validation errors --- app.js | 3 ++- src/bootstrap.js | 2 +- src/common/es-helper.js | 2 ++ src/modules/user/service.js | 12 ++++++++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 39be486..c120d45 100755 --- a/app.js +++ b/app.js @@ -2,7 +2,8 @@ * The application entry point */ -require('./src/bootstrap') +// Commenting out since logger wrapper is now set in elasticsearch wrapper +// require('./src/bootstrap') const config = require('config') const express = require('express') const cross = require('cors') diff --git a/src/bootstrap.js b/src/bootstrap.js index ef7e2e9..6d49db9 100755 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -21,7 +21,7 @@ function buildServices (dir) { let serviceName = curPath.split('modules')[1] serviceName = serviceName.substr(1, serviceName.length - 4) logger.info(`add decorates for service --> ${serviceName}`) - logger.buildService(serviceName, require(curPath)); // eslint-disable-line + logger.buildService(serviceName, require(curPath)) // eslint-disable-line } }) } diff --git a/src/common/es-helper.js b/src/common/es-helper.js index a3e40b3..8023eb8 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1145,6 +1145,8 @@ function wrapElasticSearchOp (methods, Model) { // methods: create, search, patch, get, remove const resource = getResource(Model.name) + logger.buildService(Model.name, methods) + return _.mapValues(methods, func => { if (func.name === 'search') { return async (...args) => { diff --git a/src/modules/user/service.js b/src/modules/user/service.js index a737b3c..c7160a8 100644 --- a/src/modules/user/service.js +++ b/src/modules/user/service.js @@ -7,8 +7,16 @@ const models = require('../../models/index') const helper = require('../../common/helper') const methods = helper.getServiceMethods( models.User, - { handle: joi.string().required() }, - { handle: joi.string() }, + { + 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' From c65f71035fc0a701495fdeb7931575d25cc6a042 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 15 Jun 2020 20:49:11 +0530 Subject: [PATCH 40/93] Revert "#12 - Fix issue where invalid model attributes did not raise validation errors" This reverts commit bb44d9fad2dfb9146cee3ec9368efc088474290b. --- app.js | 3 +-- src/bootstrap.js | 2 +- src/common/es-helper.js | 2 -- src/modules/user/service.js | 12 ++---------- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/app.js b/app.js index c120d45..39be486 100755 --- a/app.js +++ b/app.js @@ -2,8 +2,7 @@ * The application entry point */ -// Commenting out since logger wrapper is now set in elasticsearch wrapper -// require('./src/bootstrap') +require('./src/bootstrap') const config = require('config') const express = require('express') const cross = require('cors') diff --git a/src/bootstrap.js b/src/bootstrap.js index 6d49db9..ef7e2e9 100755 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -21,7 +21,7 @@ function buildServices (dir) { let serviceName = curPath.split('modules')[1] serviceName = serviceName.substr(1, serviceName.length - 4) logger.info(`add decorates for service --> ${serviceName}`) - logger.buildService(serviceName, require(curPath)) // eslint-disable-line + logger.buildService(serviceName, require(curPath)); // eslint-disable-line } }) } diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 8023eb8..a3e40b3 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1145,8 +1145,6 @@ function wrapElasticSearchOp (methods, Model) { // methods: create, search, patch, get, remove const resource = getResource(Model.name) - logger.buildService(Model.name, methods) - return _.mapValues(methods, func => { if (func.name === 'search') { return async (...args) => { diff --git a/src/modules/user/service.js b/src/modules/user/service.js index c7160a8..a737b3c 100644 --- a/src/modules/user/service.js +++ b/src/modules/user/service.js @@ -7,16 +7,8 @@ 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().required() }, + { handle: joi.string() }, { handle: joi.string(), roleId: joi.string() }, async query => { let prefix = 'select * from DUser' From 3b9e5730737e2629cc52cd3970f184684895d6c8 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 15 Jun 2020 20:52:31 +0530 Subject: [PATCH 41/93] Fix pagination issue where x-total header did not display total records --- src/common/es-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index a3e40b3..e055902 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1282,7 +1282,7 @@ async function searchUsers (authUser, filter, params) { user.groups = groups } - return { total: result.length, page: params.page, perPage: params.perPage, result } + return { total: getTotalCount(docs.hits.total), page: params.page, perPage: params.perPage, result } } async function searchAttributeValues ({ attributeId, attributeValue }) { @@ -1294,7 +1294,7 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { return hit.inner_hits.attributes.hits.hits[0]._source }) - return { total: result.length, result: result } + return { total: getTotalCount(esResult.hits.total), result: result } } module.exports = { From 5b5ea934c4832c444d623a327d62c3187a798436 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 16 Jun 2020 16:38:01 +0530 Subject: [PATCH 42/93] Update endpoint for deployment based on gunasekarks suggestion --- src/modules/health/route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/health/route.js b/src/modules/health/route.js index 1189977..8b11000 100644 --- a/src/modules/health/route.js +++ b/src/modules/health/route.js @@ -4,7 +4,7 @@ const Controller = require('./controller') module.exports = { - '/health': { + '/ubahnapi/health': { get: { method: Controller.get } From d4340f1e58f6db77adc6105289a03e486ade5bc8 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 16 Jun 2020 21:33:37 +0530 Subject: [PATCH 43/93] Only return unique values for attribute value lookups --- src/common/es-helper.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index e055902..bffa91f 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -701,7 +701,7 @@ function buildEsQueryFromFilter (filter) { * @param perPage maximum number of matches to return, defaults to 5 * @return {{}} created ES query object */ -function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1, perPage = 5) { +function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1, perPage = config.PAGE_SIZE) { const queryDoc = DOCUMENTS.user const matchConditions = [{ @@ -1290,11 +1290,15 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) const esResult = await esClient.search(esQuery) - const result = esResult.hits.hits.map(hit => { + + let result = esResult.hits.hits.map(hit => { return hit.inner_hits.attributes.hits.hits[0]._source }) - return { total: getTotalCount(esResult.hits.total), result: result } + result = _.uniqBy(result, 'value') // de-dupe by value + result = result.splice(0, 5) // keep only the first five matches + + return { result } } module.exports = { From c6b8f062b560bf35f7d2f88bea6d0d841ddd06e7 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 18 Jun 2020 19:11:25 +0530 Subject: [PATCH 44/93] Update keyword search to be a substring search instead of exact match. Also search on first name, last name and handle --- src/common/es-helper.js | 50 ++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index bffa91f..940bad0 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -597,11 +597,14 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } } ], - should: attribute.value.map(val => ({ - term: { - [USER_ATTRIBUTE.esDocumentValueQuery]: val + should: attribute.value.map(val => { + return { + query_string: { + default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, + query: `*${val.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + } } - })), + }), minimum_should_match: 1 } } @@ -640,6 +643,13 @@ async function searchSkills (keyword) { async function setUserSearchClausesToEsQuery (boolClause, keyword) { const skillIds = await searchSkills(keyword) + boolClause.should.push({ + query_string: { + fields: ['firstName', 'lastName', 'handle'], + query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + } + }) + boolClause.should.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, @@ -783,21 +793,27 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } }) - if (typeof filter.values === 'object') { - for (const value of filter.values) { + 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 *')}*` + } + }) } - } else { - esQueryClause.bool.should.push({ - term: { - [filter.esDocumentValueQuery]: filter.values - } - }) } + esQueryClause.bool.minimum_should_match = 1 return { @@ -815,10 +831,18 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } 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 = [ - `${filter.queryField} in (${filter.values.map(f => `'${f}'`).join(',')})` + filterValuesQuery ] + const results = await DBHelper.find(model, dbQueries) if (results.length > 0) { for (const { id } of results) { From aa9150a1ff1b23e1ae1d31c9017c344ba832f6f8 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 18 Jun 2020 22:03:03 +0530 Subject: [PATCH 45/93] Revert "Update keyword search to be a substring search instead of exact match. Also search on first name, last name and handle" This reverts commit c6b8f062b560bf35f7d2f88bea6d0d841ddd06e7. --- src/common/es-helper.js | 50 +++++++++++------------------------------ 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 940bad0..bffa91f 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -597,14 +597,11 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } } ], - should: attribute.value.map(val => { - return { - query_string: { - default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, - query: `*${val.replace(/ +/g, ' ').split(' ').join('* AND *')}*` - } + should: attribute.value.map(val => ({ + term: { + [USER_ATTRIBUTE.esDocumentValueQuery]: val } - }), + })), minimum_should_match: 1 } } @@ -643,13 +640,6 @@ async function searchSkills (keyword) { async function setUserSearchClausesToEsQuery (boolClause, keyword) { const skillIds = await searchSkills(keyword) - boolClause.should.push({ - query_string: { - fields: ['firstName', 'lastName', 'handle'], - query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* AND *')}*` - } - }) - boolClause.should.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, @@ -793,27 +783,21 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } }) - if (typeof filter.values !== 'object') { - filter.values = [filter.values] - } - - for (const value of filter.values) { - if (value === 'true' || value === 'false') { + if (typeof filter.values === 'object') { + for (const value of filter.values) { 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 *')}*` - } - }) } + } else { + esQueryClause.bool.should.push({ + term: { + [filter.esDocumentValueQuery]: filter.values + } + }) } - esQueryClause.bool.minimum_should_match = 1 return { @@ -831,18 +815,10 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } 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 + `${filter.queryField} in (${filter.values.map(f => `'${f}'`).join(',')})` ] - const results = await DBHelper.find(model, dbQueries) if (results.length > 0) { for (const { id } of results) { From f27d6f63ef13404d903e616b3006dd71ac7fd2fc Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 18 Jun 2020 22:10:22 +0530 Subject: [PATCH 46/93] Update keyword search to be a substring search instead of exact match. Also search on first name, last name and handle. Support special characters --- src/common/es-helper.js | 68 +++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index bffa91f..bfaf2b5 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -597,11 +597,14 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } } ], - should: attribute.value.map(val => ({ - term: { - [USER_ATTRIBUTE.esDocumentValueQuery]: val + should: attribute.value.map(val => { + return { + query_string: { + default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, + query: `*${val.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + } } - })), + }), minimum_should_match: 1 } } @@ -610,6 +613,11 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } } +function hasNonAlphaNumeric(text) { + const regex = /^[A-Za-z0-9 ]+$/ + return !regex.test(text) +} + /** * Get skillIds matching the search keyword * @@ -618,6 +626,9 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { */ async function searchSkills (keyword) { const queryDoc = DOCUMENTS.skill + + let query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` + const esQuery = { index: queryDoc.index, type: queryDoc.type, @@ -625,7 +636,8 @@ async function searchSkills (keyword) { query: { query_string: { default_field: 'name', - query: `*${keyword}*` + minimum_should_match: '100%', + query } }, _source: 'id' @@ -640,19 +652,26 @@ async function searchSkills (keyword) { async function setUserSearchClausesToEsQuery (boolClause, keyword) { const skillIds = await searchSkills(keyword) + boolClause.should.push({ + query_string: { + fields: ['firstName', 'lastName', 'handle'], + query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + } + }) + boolClause.should.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, query: { query_string: { - query: `*${keyword}*`, + query: hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*`, fields: [USER_ATTRIBUTE.esDocumentValueStringQuery] } } } }, { query_string: { - query: `*${keyword}*`, + query: hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*`, fields: [USER_FILTER_TO_MODEL.achievement.esDocumentValueQuery] } }) @@ -783,21 +802,27 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } }) - if (typeof filter.values === 'object') { - for (const value of filter.values) { + 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 *')}*` + } + }) } - } else { - esQueryClause.bool.should.push({ - term: { - [filter.esDocumentValueQuery]: filter.values - } - }) } + esQueryClause.bool.minimum_should_match = 1 return { @@ -815,10 +840,18 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } 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 = [ - `${filter.queryField} in (${filter.values.map(f => `'${f}'`).join(',')})` + filterValuesQuery ] + const results = await DBHelper.find(model, dbQueries) if (results.length > 0) { for (const { id } of results) { @@ -1275,6 +1308,9 @@ async function searchUsers (authUser, filter, params) { 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) { From 1b5e94e3f371ab31cc9cc04513dbfa11edd25460 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Fri, 19 Jun 2020 15:46:08 +0530 Subject: [PATCH 47/93] circleci integration --- .circleci/config.yml | 80 ++++++++++++++++++++++++++++++++++++++++++++ build.sh | 22 ++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 .circleci/config.yml create mode 100755 build.sh diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..3936f66 --- /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 \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..aa24a4b --- /dev/null +++ b/build.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -eo pipefail +APP_NAME=$1 +UPDATE_CACHE="" +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 From 5c873b9c054d8bba270dba9a9b9f6123d270e656 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 22 Jun 2020 16:43:12 +0530 Subject: [PATCH 48/93] Use aggregation query to return unique attribute values for lookup --- src/common/es-helper.js | 255 ++++++++++++++++++++++++---------------- 1 file changed, 155 insertions(+), 100 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index bfaf2b5..02cf3e6 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -38,7 +38,8 @@ const USER_ATTRIBUTE = { esDocumentPath: 'attributes', esDocumentQuery: 'attributes.attributeId.keyword', esDocumentValueStringQuery: 'attributes.value', - esDocumentValueQuery: 'attributes.value.keyword' + esDocumentValueQuery: 'attributes.value.keyword', + esDocumentId: 'attributes.id' } const USER_FILTER_TO_MODEL = { @@ -50,7 +51,9 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'skills.skillId.keyword', values: [] }, - get skills () { return this.skill }, + get skills() { + return this.skill + }, achievement: { name: 'achievement', isAttribute: false, @@ -60,7 +63,9 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'achievements.id.keyword', values: [] }, - get achievements () { return this.achievement }, + get achievements() { + return this.achievement + }, location: { name: 'location', isAttribute: true, @@ -71,7 +76,9 @@ const USER_FILTER_TO_MODEL = { defaultSortOrder: 'asc', values: [] }, - get locations () { return this.location }, + get locations() { + return this.location + }, isavailable: { name: 'isAvailable', isAttribute: true, @@ -273,7 +280,7 @@ const FILTER_CHAIN = { * @param modelName the model name * @returns {string|*} the resource */ -function getResource (modelName) { +function getResource(modelName) { if (MODEL_TO_RESOURCE[modelName]) { return MODEL_TO_RESOURCE[modelName] } else { @@ -281,11 +288,11 @@ function getResource (modelName) { } } -function getTotalCount (total) { +function getTotalCount(total) { return typeof total === 'number' ? total : total.value } -async function getOrganizationId (handle) { +async function getOrganizationId(handle) { const DBHelper = require('../models/index').DBHelper // TODO Use the service method instead of raw query @@ -302,7 +309,7 @@ async function getOrganizationId (handle) { throw new Error('User doesn\'t belong to any organization') } -async function getAttributeId (organizationId, attributeName) { +async function getAttributeId(organizationId, attributeName) { const DBHelper = require('../models/index').DBHelper // TODO Use the service method instead of raw query @@ -320,7 +327,7 @@ async function getAttributeId (organizationId, attributeName) { throw new Error(`Attribute ${attributeName} is invalid for the current users organization`) } -function parseUserFilter (params) { +function parseUserFilter(params) { const filters = {} _.forOwn(params, (value, key) => { key = key.toLowerCase() @@ -342,7 +349,7 @@ function parseUserFilter (params) { * resources which are not in user index. * @returns {{}} the resource filters keyed by resource */ -function parseEnrichFilter (params) { +function parseEnrichFilter(params) { const filters = {} _.forOwn(params, (value, key) => { if (key.includes('.')) { @@ -376,10 +383,14 @@ function parseEnrichFilter (params) { * @param item the parent object * @returns {Promise} the promise of enriched parent object */ -async function enrichResource (resource, enrichIdProp, item) { +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] }) + const subResult = await esClient.getSource({ + index: subDoc.index, + type: subDoc.type, + id: item[enrichIdProp] + }) if (filterChain.enrichNext) { const enrichIdProp = filterChain.idField @@ -398,7 +409,7 @@ async function enrichResource (resource, enrichIdProp, item) { * @param user the user object to enrich * @returns {Promise<*>} the promise of enriched user */ -async function enrichUser (user) { +async function enrichUser(user) { for (const subProp of Object.keys(SUB_DOCUMENTS)) { const subDoc = SUB_DOCUMENTS[subProp] const subData = user[subDoc.userField] @@ -418,7 +429,7 @@ async function enrichUser (user) { * @param users list of users from ES search * @returns {Promise<*>} the enriched users */ -async function enrichUsers (users) { +async function enrichUsers(users) { const enrichedUsers = [] for (const user of users) { const enriched = await enrichUser(user) @@ -433,7 +444,7 @@ async function enrichUsers (users) { * @param args the request path and query parameters * @returns {Promise<*>} the promise of retrieved resource object from ES */ -async function getFromElasticSearch (resource, ...args) { +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 @@ -489,7 +500,7 @@ async function getFromElasticSearch (resource, ...args) { * @param itself true mean the filter is applied the resource itself * @returns {[]} parsed filter array */ -function parseResourceFilter (resource, params, itself) { +function parseResourceFilter(resource, params, itself) { const resDefinedFilters = RESOURCE_FILTER[resource] const resFilters = [] if (resDefinedFilters) { @@ -524,7 +535,7 @@ function parseResourceFilter (resource, params, itself) { * @param resFilters the resource filters * @param esQuery the ES query */ -function setResourceFilterToEsQuery (resFilters, esQuery) { +function setResourceFilterToEsQuery(resFilters, esQuery) { // TODO only current res filter if (resFilters.length > 0) { for (const filter of resFilters) { @@ -550,7 +561,7 @@ function setResourceFilterToEsQuery (resFilters, esQuery) { * @param queryField the field that the filter applies * @returns {*} the ES query */ -function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) { +function setFilterValueToEsQuery(esQuery, matchField, filterValue, queryField) { if (queryField !== 'name') { matchField = matchField + '.keyword' } @@ -579,7 +590,7 @@ function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) * @param filterClause the filter clause of the ES query * @param attributes array of attribute id and value objects */ -function setUserAttributesFiltersToEsQuery (filterClause, attributes) { +function setUserAttributesFiltersToEsQuery(filterClause, attributes) { for (const attribute of attributes) { if (typeof attribute.value !== 'object') { attribute.value = [attribute.value] @@ -590,13 +601,11 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { path: USER_ATTRIBUTE.esDocumentPath, query: { bool: { - filter: [ - { - term: { - [USER_ATTRIBUTE.esDocumentQuery]: attribute.id - } + filter: [{ + term: { + [USER_ATTRIBUTE.esDocumentQuery]: attribute.id } - ], + }], should: attribute.value.map(val => { return { query_string: { @@ -624,10 +633,10 @@ function hasNonAlphaNumeric(text) { * @param keyword the search keyword * @returns array of skillIds */ -async function searchSkills (keyword) { +async function searchSkills(keyword) { const queryDoc = DOCUMENTS.skill - let query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` + const query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` const esQuery = { index: queryDoc.index, @@ -649,7 +658,7 @@ async function searchSkills (keyword) { return results.hits.hits.map(hit => hit._source.id) } -async function setUserSearchClausesToEsQuery (boolClause, keyword) { +async function setUserSearchClausesToEsQuery(boolClause, keyword) { const skillIds = await searchSkills(keyword) boolClause.should.push({ @@ -692,7 +701,7 @@ async function setUserSearchClausesToEsQuery (boolClause, keyword) { * @param filter * @returns {{}} created ES query object */ -function buildEsQueryFromFilter (filter) { +function buildEsQueryFromFilter(filter) { const queryDoc = DOCUMENTS[filter.resource] const esQuery = { index: queryDoc.index, @@ -716,61 +725,51 @@ function buildEsQueryFromFilter (filter) { * 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 page result page, defaults to 1 - * @param perPage maximum number of matches to return, defaults to 5 + * @param size the number of attributes to fetch * @return {{}} created ES query object */ -function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1, perPage = config.PAGE_SIZE) { +function buildEsQueryToGetAttributeValues(attributeId, attributeValue, size) { const queryDoc = DOCUMENTS.user - const matchConditions = [{ - match: { - [USER_ATTRIBUTE.esDocumentQuery]: attributeId - } - }] - - if (attributeValue !== null) { - matchConditions.push({ - query_string: { - default_field: USER_ATTRIBUTE.esDocumentValueStringQuery, - // minimum_should_match: `100%`, /* disallow misspellings */ - query: `*${attributeValue}*` - } - }) - } - const esQuery = { index: queryDoc.index, type: queryDoc.type, body: { - query: { - nested: { - path: USER_ATTRIBUTE.esDocumentPath, - query: { - bool: { - must: matchConditions - } - }, - inner_hits: {} // Get the attriute values - } - }, - sort: [{ - [USER_ATTRIBUTE.esDocumentValueQuery]: { - order: 'asc', + size: 0, + aggs: { + attributes: { nested: { - path: USER_ATTRIBUTE.esDocumentPath, - filter: { - term: { - [USER_ATTRIBUTE.esDocumentQuery]: attributeId + 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 => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, + order: { + _key: 'asc' + }, + size: size || 1000 + }, + aggs: { + attribute: { + top_hits: { + size: 1, + _source: USER_ATTRIBUTE.esDocumentId + } + } + } + } } } } } - }], - size: perPage, - from: (page - 1) * perPage, - _source: { - excludes: '*' // Parent document matches aren't required } } } @@ -778,7 +777,9 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, page = 1 return esQuery } -async function resolveUserFilterFromDb (filter, { handle }, organizationId) { +async function resolveUserFilterFromDb(filter, { + handle +}, organizationId) { const DBHelper = require('../models/index').DBHelper if (filter.isAttribute) { @@ -854,7 +855,9 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { const results = await DBHelper.find(model, dbQueries) if (results.length > 0) { - for (const { id } of results) { + for (const { + id + } of results) { esQueryClause.bool.should.push({ term: { [filter.esDocumentQuery]: id @@ -869,7 +872,9 @@ async function resolveUserFilterFromDb (filter, { handle }, organizationId) { } } -async function resolveSortClauseFromDb (orderBy, { handle }, organizationId) { +async function resolveSortClauseFromDb(orderBy, { + handle +}, organizationId) { if (orderBy === 'name') { return [{ 'firstName.keyword': 'asc' @@ -889,7 +894,9 @@ async function resolveSortClauseFromDb (orderBy, { handle }, organizationId) { nested: { path: attribute.esDocumentPath, filter: { - term: { [`${attribute.esDocumentQuery}`]: await getAttributeId(organizationId, attribute.attributeName) } + term: { + [`${attribute.esDocumentQuery}`]: await getAttributeId(organizationId, attribute.attributeName) + } } } } @@ -906,7 +913,7 @@ async function resolveSortClauseFromDb (orderBy, { handle }, organizationId) { * @param initialRes the resource that initial request comes in * @returns {Promise<*>} the resolved value */ -async function resolveResFilter (filter, initialRes) { +async function resolveResFilter(filter, initialRes) { const doc = DOCUMENTS[filter.resource] const filterChain = FILTER_CHAIN[filter.resource] @@ -954,7 +961,7 @@ async function resolveResFilter (filter, initialRes) { * @param perPage * @returns {*} */ -function applySubResFilters (results, preResFilterResults, ownResFilters, perPage) { +function applySubResFilters(results, preResFilterResults, ownResFilters, perPage) { let count = 0 const filtered = results.filter(item => { for (const filter of preResFilterResults) { @@ -982,8 +989,11 @@ function applySubResFilters (results, preResFilterResults, ownResFilters, perPag * @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') +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] @@ -1127,7 +1137,12 @@ async function searchElasticSearch (resource, ...args) { 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: [] } + return { + total: docs.hits.total, + page: params.page, + perPage: params.perPage, + result: [] + } } let result = [] @@ -1148,11 +1163,18 @@ async function searchElasticSearch (resource, ...args) { result = docs.hits.hits.map(hit => hit._source) } - return { total: docs.hits.total, page: params.page, perPage: params.perPage, result } + return { + total: docs.hits.total, + page: params.page, + perPage: params.perPage, + result + } } -async function publishMessage (op, resource, result) { - const { postEvent } = require('./helper') +async function publishMessage(op, resource, result) { + const { + postEvent + } = require('./helper') if (!OP_TO_TOPIC[op]) { logger.warn(`Invalid operation: ${op}`) @@ -1162,7 +1184,9 @@ async function publishMessage (op, resource, result) { 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: resource }, result)) + await postEvent(OP_TO_TOPIC[op], _.assign({ + resource: resource + }, result)) } /** @@ -1172,7 +1196,7 @@ async function publishMessage (op, resource, result) { * @param Model the model to access * @returns {{}} ES wrapped CRUD methods */ -function wrapElasticSearchOp (methods, Model) { +function wrapElasticSearchOp(methods, Model) { logger.info('Decorating ES to API methods') // methods: create, search, patch, get, remove @@ -1189,14 +1213,23 @@ function wrapElasticSearchOp (methods, Model) { throw errors.elasticSearchEnrichError(err.message) } logger.logFullError(err) - const { items, meta } = await func(...args) + const { + items, + meta + } = await func(...args) // 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, total: meta.total, result: items } + return { + fromDB: true, + total: meta.total, + result: items + } } } } else if (func.name === 'get') { - const { permissionCheck } = require('./helper') + const { + permissionCheck + } = require('./helper') return async (...args) => { if (args[3]) { // merge query to params if exists. req.query was added at the end not to break the existing QLDB code. @@ -1242,8 +1275,11 @@ function wrapElasticSearchOp (methods, Model) { }) } -async function searchUsers (authUser, filter, params) { - const { checkIfExists, getAuthUser } = require('./helper') +async function searchUsers(authUser, filter, params) { + const { + checkIfExists, + getAuthUser + } = require('./helper') const queryDoc = DOCUMENTS.user if (!params.page) { @@ -1318,27 +1354,46 @@ async function searchUsers (authUser, filter, params) { user.groups = groups } - return { total: getTotalCount(docs.hits.total), page: params.page, perPage: params.perPage, result } + return { + total: getTotalCount(docs.hits.total), + page: params.page, + perPage: params.perPage, + result + } } -async function searchAttributeValues ({ attributeId, attributeValue }) { - const esQuery = buildEsQueryToGetAttributeValues(attributeId, attributeValue) +async function searchAttributeValues({ + attributeId, + attributeValue +}) { + const esQuery = buildEsQueryToGetAttributeValues(attributeId, attributeValue, 5) logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) const esResult = await esClient.search(esQuery) - let result = esResult.hits.hits.map(hit => { - return hit.inner_hits.attributes.hits.hits[0]._source - }) + const result = [] + const attributes = esResult.aggregations.attributes.ids.buckets - result = _.uniqBy(result, 'value') // de-dupe by value - result = result.splice(0, 5) // keep only the first five matches + 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 } + return { + result + } } module.exports = { wrapElasticSearchOp, searchUsers, searchAttributeValues -} +} \ No newline at end of file From fd2daf95b722fe198f917434794722022d098fa5 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 22 Jun 2020 17:29:06 +0530 Subject: [PATCH 49/93] Provide filter by organizationId --- src/common/es-helper.js | 123 ++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 68 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 02cf3e6..534508a 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -42,6 +42,10 @@ const USER_ATTRIBUTE = { esDocumentId: 'attributes.id' } +const USER_ORGANIZATION = { + esOrganizationQuery: 'externalProfiles.organizationId.keyword' +} + const USER_FILTER_TO_MODEL = { skill: { name: 'skill', @@ -51,9 +55,7 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'skills.skillId.keyword', values: [] }, - get skills() { - return this.skill - }, + get skills () { return this.skill }, achievement: { name: 'achievement', isAttribute: false, @@ -63,9 +65,7 @@ const USER_FILTER_TO_MODEL = { esDocumentQuery: 'achievements.id.keyword', values: [] }, - get achievements() { - return this.achievement - }, + get achievements () { return this.achievement }, location: { name: 'location', isAttribute: true, @@ -76,9 +76,7 @@ const USER_FILTER_TO_MODEL = { defaultSortOrder: 'asc', values: [] }, - get locations() { - return this.location - }, + get locations () { return this.location }, isavailable: { name: 'isAvailable', isAttribute: true, @@ -280,7 +278,7 @@ const FILTER_CHAIN = { * @param modelName the model name * @returns {string|*} the resource */ -function getResource(modelName) { +function getResource (modelName) { if (MODEL_TO_RESOURCE[modelName]) { return MODEL_TO_RESOURCE[modelName] } else { @@ -288,11 +286,11 @@ function getResource(modelName) { } } -function getTotalCount(total) { +function getTotalCount (total) { return typeof total === 'number' ? total : total.value } -async function getOrganizationId(handle) { +async function getOrganizationId (handle) { const DBHelper = require('../models/index').DBHelper // TODO Use the service method instead of raw query @@ -309,7 +307,7 @@ async function getOrganizationId(handle) { throw new Error('User doesn\'t belong to any organization') } -async function getAttributeId(organizationId, attributeName) { +async function getAttributeId (organizationId, attributeName) { const DBHelper = require('../models/index').DBHelper // TODO Use the service method instead of raw query @@ -327,7 +325,7 @@ async function getAttributeId(organizationId, attributeName) { throw new Error(`Attribute ${attributeName} is invalid for the current users organization`) } -function parseUserFilter(params) { +function parseUserFilter (params) { const filters = {} _.forOwn(params, (value, key) => { key = key.toLowerCase() @@ -349,7 +347,7 @@ function parseUserFilter(params) { * resources which are not in user index. * @returns {{}} the resource filters keyed by resource */ -function parseEnrichFilter(params) { +function parseEnrichFilter (params) { const filters = {} _.forOwn(params, (value, key) => { if (key.includes('.')) { @@ -383,7 +381,7 @@ function parseEnrichFilter(params) { * @param item the parent object * @returns {Promise} the promise of enriched parent object */ -async function enrichResource(resource, enrichIdProp, item) { +async function enrichResource (resource, enrichIdProp, item) { const subDoc = DOCUMENTS[resource] const filterChain = FILTER_CHAIN[resource] const subResult = await esClient.getSource({ @@ -409,7 +407,7 @@ async function enrichResource(resource, enrichIdProp, item) { * @param user the user object to enrich * @returns {Promise<*>} the promise of enriched user */ -async function enrichUser(user) { +async function enrichUser (user) { for (const subProp of Object.keys(SUB_DOCUMENTS)) { const subDoc = SUB_DOCUMENTS[subProp] const subData = user[subDoc.userField] @@ -429,7 +427,7 @@ async function enrichUser(user) { * @param users list of users from ES search * @returns {Promise<*>} the enriched users */ -async function enrichUsers(users) { +async function enrichUsers (users) { const enrichedUsers = [] for (const user of users) { const enriched = await enrichUser(user) @@ -444,7 +442,7 @@ async function enrichUsers(users) { * @param args the request path and query parameters * @returns {Promise<*>} the promise of retrieved resource object from ES */ -async function getFromElasticSearch(resource, ...args) { +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 @@ -500,7 +498,7 @@ async function getFromElasticSearch(resource, ...args) { * @param itself true mean the filter is applied the resource itself * @returns {[]} parsed filter array */ -function parseResourceFilter(resource, params, itself) { +function parseResourceFilter (resource, params, itself) { const resDefinedFilters = RESOURCE_FILTER[resource] const resFilters = [] if (resDefinedFilters) { @@ -535,7 +533,7 @@ function parseResourceFilter(resource, params, itself) { * @param resFilters the resource filters * @param esQuery the ES query */ -function setResourceFilterToEsQuery(resFilters, esQuery) { +function setResourceFilterToEsQuery (resFilters, esQuery) { // TODO only current res filter if (resFilters.length > 0) { for (const filter of resFilters) { @@ -561,7 +559,7 @@ function setResourceFilterToEsQuery(resFilters, esQuery) { * @param queryField the field that the filter applies * @returns {*} the ES query */ -function setFilterValueToEsQuery(esQuery, matchField, filterValue, queryField) { +function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) { if (queryField !== 'name') { matchField = matchField + '.keyword' } @@ -590,7 +588,7 @@ function setFilterValueToEsQuery(esQuery, matchField, filterValue, queryField) { * @param filterClause the filter clause of the ES query * @param attributes array of attribute id and value objects */ -function setUserAttributesFiltersToEsQuery(filterClause, attributes) { +function setUserAttributesFiltersToEsQuery (filterClause, attributes) { for (const attribute of attributes) { if (typeof attribute.value !== 'object') { attribute.value = [attribute.value] @@ -622,7 +620,15 @@ function setUserAttributesFiltersToEsQuery(filterClause, attributes) { } } -function hasNonAlphaNumeric(text) { +function setUserOrganizationFiilterToEsQuery (filterClause, organizationId) { + filterClause.push({ + term: { + [USER_ORGANIZATION.esOrganizationQuery]: organizationId + } + }) +} + +function hasNonAlphaNumeric (text) { const regex = /^[A-Za-z0-9 ]+$/ return !regex.test(text) } @@ -633,7 +639,7 @@ function hasNonAlphaNumeric(text) { * @param keyword the search keyword * @returns array of skillIds */ -async function searchSkills(keyword) { +async function searchSkills (keyword) { const queryDoc = DOCUMENTS.skill const query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` @@ -658,7 +664,7 @@ async function searchSkills(keyword) { return results.hits.hits.map(hit => hit._source.id) } -async function setUserSearchClausesToEsQuery(boolClause, keyword) { +async function setUserSearchClausesToEsQuery (boolClause, keyword) { const skillIds = await searchSkills(keyword) boolClause.should.push({ @@ -701,7 +707,7 @@ async function setUserSearchClausesToEsQuery(boolClause, keyword) { * @param filter * @returns {{}} created ES query object */ -function buildEsQueryFromFilter(filter) { +function buildEsQueryFromFilter (filter) { const queryDoc = DOCUMENTS[filter.resource] const esQuery = { index: queryDoc.index, @@ -728,7 +734,7 @@ function buildEsQueryFromFilter(filter) { * @param size the number of attributes to fetch * @return {{}} created ES query object */ -function buildEsQueryToGetAttributeValues(attributeId, attributeValue, size) { +function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) { const queryDoc = DOCUMENTS.user const esQuery = { @@ -777,9 +783,7 @@ function buildEsQueryToGetAttributeValues(attributeId, attributeValue, size) { return esQuery } -async function resolveUserFilterFromDb(filter, { - handle -}, organizationId) { +async function resolveUserFilterFromDb (filter, { handle }, organizationId) { const DBHelper = require('../models/index').DBHelper if (filter.isAttribute) { @@ -855,9 +859,7 @@ async function resolveUserFilterFromDb(filter, { const results = await DBHelper.find(model, dbQueries) if (results.length > 0) { - for (const { - id - } of results) { + for (const { id } of results) { esQueryClause.bool.should.push({ term: { [filter.esDocumentQuery]: id @@ -872,9 +874,7 @@ async function resolveUserFilterFromDb(filter, { } } -async function resolveSortClauseFromDb(orderBy, { - handle -}, organizationId) { +async function resolveSortClauseFromDb (orderBy, { handle }, organizationId) { if (orderBy === 'name') { return [{ 'firstName.keyword': 'asc' @@ -913,7 +913,7 @@ async function resolveSortClauseFromDb(orderBy, { * @param initialRes the resource that initial request comes in * @returns {Promise<*>} the resolved value */ -async function resolveResFilter(filter, initialRes) { +async function resolveResFilter (filter, initialRes) { const doc = DOCUMENTS[filter.resource] const filterChain = FILTER_CHAIN[filter.resource] @@ -961,7 +961,7 @@ async function resolveResFilter(filter, initialRes) { * @param perPage * @returns {*} */ -function applySubResFilters(results, preResFilterResults, ownResFilters, perPage) { +function applySubResFilters (results, preResFilterResults, ownResFilters, perPage) { let count = 0 const filtered = results.filter(item => { for (const filter of preResFilterResults) { @@ -989,7 +989,7 @@ function applySubResFilters(results, preResFilterResults, ownResFilters, perPage * @param args the request path and query parameters * @returns {Promise<*>} the promise of searched results */ -async function searchElasticSearch(resource, ...args) { +async function searchElasticSearch (resource, ...args) { const { checkIfExists, getAuthUser @@ -1171,10 +1171,8 @@ async function searchElasticSearch(resource, ...args) { } } -async function publishMessage(op, resource, result) { - const { - postEvent - } = require('./helper') +async function publishMessage (op, resource, result) { + const { postEvent } = require('./helper') if (!OP_TO_TOPIC[op]) { logger.warn(`Invalid operation: ${op}`) @@ -1184,9 +1182,7 @@ async function publishMessage(op, resource, result) { 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: resource - }, result)) + await postEvent(OP_TO_TOPIC[op], _.assign({ resource }, result)) } /** @@ -1196,7 +1192,7 @@ async function publishMessage(op, resource, result) { * @param Model the model to access * @returns {{}} ES wrapped CRUD methods */ -function wrapElasticSearchOp(methods, Model) { +function wrapElasticSearchOp (methods, Model) { logger.info('Decorating ES to API methods') // methods: create, search, patch, get, remove @@ -1213,10 +1209,7 @@ function wrapElasticSearchOp(methods, Model) { throw errors.elasticSearchEnrichError(err.message) } logger.logFullError(err) - const { - items, - meta - } = await func(...args) + const { items, meta } = await func(...args) // 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 { @@ -1227,9 +1220,7 @@ function wrapElasticSearchOp(methods, Model) { } } } else if (func.name === 'get') { - const { - permissionCheck - } = require('./helper') + const { permissionCheck } = require('./helper') return async (...args) => { if (args[3]) { // merge query to params if exists. req.query was added at the end not to break the existing QLDB code. @@ -1275,11 +1266,8 @@ function wrapElasticSearchOp(methods, Model) { }) } -async function searchUsers(authUser, filter, params) { - const { - checkIfExists, - getAuthUser - } = require('./helper') +async function searchUsers (authUser, filter, params) { + const { checkIfExists, getAuthUser } = require('./helper') const queryDoc = DOCUMENTS.user if (!params.page) { @@ -1340,6 +1328,10 @@ async function searchUsers(authUser, filter, params) { setUserAttributesFiltersToEsQuery(esQuery.body.query.bool.filter, filter.attributes) } + if (filter.organizationId != null) { + setUserOrganizationFiilterToEsQuery(esQuery.body.query.bool.filter, filter.organizationId) + } + logger.debug(`ES query for searching users: ${JSON.stringify(esQuery, null, 2)}`) const docs = await esClient.search(esQuery) @@ -1362,10 +1354,7 @@ async function searchUsers(authUser, filter, params) { } } -async function searchAttributeValues({ - attributeId, - attributeValue -}) { +async function searchAttributeValues ({ attributeId, attributeValue }) { const esQuery = buildEsQueryToGetAttributeValues(attributeId, attributeValue, 5) logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) @@ -1387,13 +1376,11 @@ async function searchAttributeValues({ } } - return { - result - } + return { result } } module.exports = { wrapElasticSearchOp, searchUsers, searchAttributeValues -} \ No newline at end of file +} From cb1458c7137542c534b2d58b44c23295f7c498e3 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 28 Jun 2020 19:37:03 +0530 Subject: [PATCH 50/93] #12 - Fix issue where payloads would not be validated. Also, support inactive users --- scripts/db/data/ExternalProfile.json | 40 ++++++ scripts/db/data/Organization.json | 8 ++ scripts/db/data/UserAttribute.json | 14 +-- src/bootstrap.js | 4 +- src/common/es-helper.js | 163 +++++-------------------- src/common/service-helper.js | 124 +++++++++++++++++-- src/models/ExternalProfile.js | 1 + src/modules/externalProfile/service.js | 14 ++- src/modules/user/service.js | 12 +- 9 files changed, 224 insertions(+), 156 deletions(-) diff --git a/scripts/db/data/ExternalProfile.json b/scripts/db/data/ExternalProfile.json index df49d2d..0cd4563 100644 --- a/scripts/db/data/ExternalProfile.json +++ b/scripts/db/data/ExternalProfile.json @@ -5,6 +5,7 @@ "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", @@ -16,6 +17,7 @@ "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", @@ -27,6 +29,19 @@ "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", @@ -38,6 +53,7 @@ "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", @@ -49,6 +65,7 @@ "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", @@ -60,6 +77,7 @@ "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", @@ -71,6 +89,7 @@ "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", @@ -82,6 +101,7 @@ "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", @@ -93,6 +113,7 @@ "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", @@ -104,6 +125,7 @@ "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", @@ -115,6 +137,7 @@ "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", @@ -126,6 +149,7 @@ "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", @@ -137,6 +161,7 @@ "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", @@ -148,6 +173,7 @@ "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", @@ -159,6 +185,7 @@ "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", @@ -170,6 +197,7 @@ "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", @@ -181,6 +209,7 @@ "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", @@ -192,6 +221,7 @@ "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", @@ -203,6 +233,7 @@ "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", @@ -214,6 +245,7 @@ "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", @@ -225,6 +257,7 @@ "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", @@ -236,6 +269,7 @@ "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", @@ -247,6 +281,7 @@ "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", @@ -258,6 +293,7 @@ "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", @@ -269,6 +305,7 @@ "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", @@ -280,6 +317,7 @@ "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", @@ -291,6 +329,7 @@ "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", @@ -302,6 +341,7 @@ "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", diff --git a/scripts/db/data/Organization.json b/scripts/db/data/Organization.json index 6859578..62dc93d 100644 --- a/scripts/db/data/Organization.json +++ b/scripts/db/data/Organization.json @@ -6,5 +6,13 @@ "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/UserAttribute.json b/scripts/db/data/UserAttribute.json index c370b5d..5389d7a 100644 --- a/scripts/db/data/UserAttribute.json +++ b/scripts/db/data/UserAttribute.json @@ -80,7 +80,7 @@ "userId": "ce348067-e73f-49d7-af72-fcf11a6c88bf" }, { - "id": "20721207-c868-4ee4-8ac4-59db683b7bce", + "id": "f15010ef-76fd-4a70-aa7c-a53200300300", "created": "2020-05-13T08:48:30.541Z", "updated": null, "createdBy": "tc-user", @@ -90,7 +90,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "efcbd4a7-4e1c-4109-8fac-50a1d6d5b7df", + "id": "208386ea-b3d7-4add-ab6c-6460b0ae7fab", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", @@ -100,7 +100,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "cd28194f-c3a9-4445-bef0-ea411996cc93", + "id": "edb933cc-e2fb-4112-a2fc-6809af7733c5", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", @@ -120,7 +120,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "90fef208-619d-49c6-890e-c789664b28d7", + "id": "21de9324-900d-41ea-b127-f297dfb9a873", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", @@ -130,7 +130,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "8a174a2b-cda3-463d-b3ef-41367d983258", + "id": "b93ebc48-8d24-4ea9-8d4b-08fd00b20901", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", @@ -140,7 +140,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "17ff7dcb-8751-4fe1-bb3c-c2b00b447d3c", + "id": "cfd55fff-4b21-48ce-bcc6-fd031f20ebe5", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", @@ -150,7 +150,7 @@ "userId": "0bcb0d86-09bb-410a-b2b1-fba90d1a7699" }, { - "id": "15e7bcf1-aac7-4718-ac11-6f2b94be3d33", + "id": "86b5945d-287b-462a-9a47-4e506f03f2e6", "created": "2020-05-13T08:19:13.709Z", "updated": null, "createdBy": "tc-Admin", diff --git a/src/bootstrap.js b/src/bootstrap.js index ef7e2e9..d4bf725 100755 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -20,8 +20,8 @@ function buildServices (dir) { } else if (file.toLowerCase().indexOf('service.js') >= 0) { let serviceName = curPath.split('modules')[1] serviceName = serviceName.substr(1, serviceName.length - 4) - logger.info(`add decorates for service --> ${serviceName}`) - logger.buildService(serviceName, require(curPath)); // eslint-disable-line + logger.info(`add decorators for service --> ${serviceName}`) + logger.buildService(serviceName, require(curPath)); // eslint-disable-line } }) } diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 534508a..f9ea4bb 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1,7 +1,6 @@ const config = require('config') const _ = require('lodash') const logger = require('../common/logger') -const errors = require('./errors') const groupApi = require('./group-api') const appConst = require('../consts') const esClient = require('./es-client').getESClient() @@ -18,22 +17,6 @@ _.forOwn(DOCUMENTS, (value, key) => { } }) -// mapping operation to topic -const OP_TO_TOPIC = { - create: config.UBAHN_CREATE_TOPIC, - patch: config.UBAHN_UPDATE_TOPIC, - remove: config.UBAHN_DELETE_TOPIC -} - -// map model name to bus message resource if different -const MODEL_TO_RESOURCE = { - UsersSkill: 'userskill', - SkillsProvider: 'skillprovider', - AchievementsProvider: 'achievementprovider', - UsersAttribute: 'userattribute', - UsersRole: 'userrole' -} - const USER_ATTRIBUTE = { esDocumentPath: 'attributes', esDocumentQuery: 'attributes.attributeId.keyword', @@ -111,11 +94,6 @@ const RESOURCE_FILTER = { resource: 'userrole', queryField: 'roleId' } - // TODO usergroup/group resource is not implemented yet - // groupId: { - // resource: 'usergroup', - // queryField: 'groupId' - // } }, role: { name: { @@ -182,6 +160,14 @@ const RESOURCE_FILTER = { organizationName: { resource: 'organization', queryField: 'name' + }, + externalId: { + resource: 'externalprofile', + queryField: 'externalId' + }, + isInactive: { + resource: 'externalprofile', + queryField: 'isInactive' } }, achievement: { @@ -273,19 +259,6 @@ const FILTER_CHAIN = { } } -/** - * 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() - } -} - function getTotalCount (total) { return typeof total === 'number' ? total : total.value } @@ -534,12 +507,11 @@ function parseResourceFilter (resource, params, itself) { * @param esQuery the ES query */ function setResourceFilterToEsQuery (resFilters, esQuery) { - // TODO only current res filter if (resFilters.length > 0) { for (const filter of resFilters) { const doc = DOCUMENTS[filter.resource] - let matchField = doc.userField ? `${doc.userField}.${doc.queryField}` : `${filter.queryField}` - if (filter.queryField !== 'name') { + let matchField = doc.userField ? `${doc.userField}.${filter.queryField}` : `${filter.queryField}` + if (filter.queryField !== 'name' && filter.queryField !== 'isInactive') { matchField = matchField + '.keyword' } esQuery.body.query.bool.must.push({ @@ -560,7 +532,7 @@ function setResourceFilterToEsQuery (resFilters, esQuery) { * @returns {*} the ES query */ function setFilterValueToEsQuery (esQuery, matchField, filterValue, queryField) { - if (queryField !== 'name') { + if (queryField !== 'name' && queryField !== 'isInactive') { matchField = matchField + '.keyword' } if (Array.isArray(filterValue)) { @@ -1171,101 +1143,14 @@ async function searchElasticSearch (resource, ...args) { } } -async function publishMessage (op, resource, result) { - const { postEvent } = require('./helper') - - if (!OP_TO_TOPIC[op]) { - logger.warn(`Invalid operation: ${op}`) - return - } - - 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)) -} - /** - * Wrap QLDB methods with ES methods. Specifically read ES before QLDB for read operations, publish message - * after QLDB for write operations. - * @param methods QLDB CRUD methods - * @param Model the model to access - * @returns {{}} ES wrapped CRUD methods + * 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 */ -function wrapElasticSearchOp (methods, Model) { - logger.info('Decorating ES to API methods') - - // methods: create, search, patch, get, remove - const resource = getResource(Model.name) - - return _.mapValues(methods, func => { - if (func.name === 'search') { - return async (...args) => { - try { - return await searchElasticSearch(resource, ...args) - } catch (err) { - // return error if enrich fails - if (resource === 'user' && args[0].enrich) { - throw errors.elasticSearchEnrichError(err.message) - } - logger.logFullError(err) - const { items, meta } = await func(...args) - // 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, - total: meta.total, - result: items - } - } - } - } else if (func.name === 'get') { - const { permissionCheck } = require('./helper') - return async (...args) => { - if (args[3]) { - // merge query to params if exists. req.query was added at the end not to break the existing QLDB code. - args[2] = _.assign(args[2], args[3]) - } - try { - const result = await getFromElasticSearch(resource, ...args) - // check permission - const authUser = args[1] - permissionCheck(authUser, result) - return result - } catch (err) { - // return error if enrich fails or permission fails - if ((resource === 'user' && args[2].enrich) || (err.status && err.status === 403)) { - throw errors.elasticSearchEnrichError(err.message) - } - logger.logFullError(err) - const result = await func(...args) - return result - } - } - } else { - return async (...args) => { - let result = await func(...args) - // remove action returns undefined, pass id to elasticsearch - if (func.name === 'remove') { - if (SUB_DOCUMENTS[resource]) { - result = _.assign({}, args[2]) - } else { - result = { - id: args[0] - } - } - } - try { - await publishMessage(func.name, resource, result) - } catch (err) { - logger.logFullError(err) - } - return result - } - } - }) -} - async function searchUsers (authUser, filter, params) { const { checkIfExists, getAuthUser } = require('./helper') const queryDoc = DOCUMENTS.user @@ -1332,6 +1217,13 @@ async function searchUsers (authUser, filter, params) { 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) @@ -1354,6 +1246,10 @@ async function searchUsers (authUser, filter, params) { } } +/** + * 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, attributeValue, 5) logger.debug(`ES query for searching attribute values: ${JSON.stringify(esQuery, null, 2)}`) @@ -1380,7 +1276,8 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { } module.exports = { - wrapElasticSearchOp, + searchElasticSearch, + getFromElasticSearch, searchUsers, searchAttributeValues } diff --git a/src/common/service-helper.js b/src/common/service-helper.js index 2d64c5f..57f58b3 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -1,8 +1,47 @@ 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_DOCUMENTS = {} +_.forOwn(config.ES.DOCUMENTS, (value, key) => { + if (value.userField) { + SUB_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' +} + +/** + * 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 @@ -73,6 +112,26 @@ function buildQueryByParams (params) { 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 @@ -85,6 +144,7 @@ function buildQueryByParams (params) { */ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buildDBQuery, uniqueFields) { const models = require('../models/index') + const resource = getResource(Model.name) const { permissionCheck, checkIfExists, getAuthUser } = require('./helper') /** @@ -102,6 +162,11 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil 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 } @@ -128,6 +193,11 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil 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 } @@ -146,15 +216,29 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil * @param query the query parameters * @return {Promise} the db device */ - async function get (id, auth, params, query) { + async function get (id, auth, params, query = {}) { let recordObj - if (_.isNil(params) || _.isEmpty(params)) { + // 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(params)) + const items = await models.DBHelper.find(Model, buildQueryByParams(trueParams)) recordObj = items[0] if (!recordObj) { - throw errors.newEntityNotFoundError(`cannot find ${Model.tableName} where ${_.map(params, (v, k) => `${k}:${v}`).join(', ')}`) + throw errors.newEntityNotFoundError(`cannot find ${Model.tableName} where ${_.map(trueParams, (v, k) => `${k}:${v}`).join(', ')}`) } } permissionCheck(auth, recordObj) @@ -168,6 +252,17 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil * @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 @@ -176,7 +271,9 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil dbQueries.push(`${Model.tableName}.createdBy = '${getAuthUser(auth)}'`) } const items = await models.DBHelper.find(Model, dbQueries) - return { items, meta: { total: items.length } } + // 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 = { @@ -192,15 +289,26 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil * @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_DOCUMENTS[resource]) { + payload = _.assign({}, params) + } else { + payload = { + id + } + } + try { + await publishMessage('remove', resource, payload) + } catch (err) { + logger.logFullError(err) + } } - const methods = { + return { create, search, patch, get, remove } - - return esHelper.wrapElasticSearchOp(methods, Model) } module.exports = { diff --git a/src/models/ExternalProfile.js b/src/models/ExternalProfile.js index 5b8d720..a847499 100644 --- a/src/models/ExternalProfile.js +++ b/src/models/ExternalProfile.js @@ -10,6 +10,7 @@ class ExternalProfile extends RecordObject { this.organizationId = null this.externalId = null this.uri = null + this.isInactive = null } } diff --git a/src/modules/externalProfile/service.js b/src/modules/externalProfile/service.js index 8cfcfe1..678314d 100644 --- a/src/modules/externalProfile/service.js +++ b/src/modules/externalProfile/service.js @@ -11,18 +11,21 @@ const methods = helper.getServiceMethods( userId: joi.string().required(), organizationId: joi.string().required(), externalId: joi.string().required(), - uri: joi.string().required() + uri: joi.string().required(), + isInactive: joi.boolean().default(false) }, { // patch request body joi schema userId: joi.string().required(), organizationId: joi.string().required(), externalId: joi.string(), - uri: joi.string() + uri: joi.string(), + isInactive: joi.boolean() }, { // search request query joi schema userId: joi.string().required(), externalId: joi.string(), - organizationName: joi.string() + organizationName: joi.string(), + isInactive: joi.boolean() }, async (query) => { // build search query by request const dbQueries = ['SELECT * FROM Organization, ExternalProfile', @@ -33,7 +36,10 @@ const methods = helper.getServiceMethods( dbQueries.push(`Organization.name like '%${query.organizationName}%'`) } if (query.externalId) { - dbQueries.push(`ExternalProfile.externalId like '%${query.externalId}%'`) + dbQueries.push(`ExternalProfile.externalId = '${query.externalId}'`) + } + if (query.isInactive) { + dbQueries.push(`ExternalProfile.isInactive = '${query.isInactive}'`) } return dbQueries diff --git a/src/modules/user/service.js b/src/modules/user/service.js index a737b3c..c7160a8 100644 --- a/src/modules/user/service.js +++ b/src/modules/user/service.js @@ -7,8 +7,16 @@ const models = require('../../models/index') const helper = require('../../common/helper') const methods = helper.getServiceMethods( models.User, - { handle: joi.string().required() }, - { handle: joi.string() }, + { + 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' From 2787856171593ca293df775524fcc2de2d9a5177 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 29 Jun 2020 12:27:42 +0530 Subject: [PATCH 51/93] Delete index before creating --- scripts/db/genData.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/db/genData.js b/scripts/db/genData.js index fea3401..d4764fa 100644 --- a/scripts/db/genData.js +++ b/scripts/db/genData.js @@ -16,6 +16,13 @@ async function insertIntoES (modelName, body) { const client = getESClient() if (_.includes(_.keys(topResources), esResourceName)) { + try { + await client.indices.delete({ + index: topResources[esResourceName].index + }) + } catch (error) { + // Ignore error. Indice might not exist + } await client.create({ index: topResources[esResourceName].index, type: topResources[esResourceName].type, From 2177a7c1f267aad6a28827340cd7f17a6deb233b Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 29 Jun 2020 14:48:26 +0530 Subject: [PATCH 52/93] undo previous commit due to error in inserting data for sub resources --- scripts/db/genData.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/scripts/db/genData.js b/scripts/db/genData.js index d4764fa..fea3401 100644 --- a/scripts/db/genData.js +++ b/scripts/db/genData.js @@ -16,13 +16,6 @@ async function insertIntoES (modelName, body) { const client = getESClient() if (_.includes(_.keys(topResources), esResourceName)) { - try { - await client.indices.delete({ - index: topResources[esResourceName].index - }) - } catch (error) { - // Ignore error. Indice might not exist - } await client.create({ index: topResources[esResourceName].index, type: topResources[esResourceName].type, From 355015f46090e94f95d2cf9e3c5fd4363fb8c09f Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 29 Jun 2020 15:33:01 +0530 Subject: [PATCH 53/93] Trigger heroku deploy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dcce81..4dde100 100755 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ you can use below token to test role and permissions - token - ```json + ```text eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLXVzZXIiLCJleHAiOjE2ODU1NzE0NjAsInVzZXJJZCI6IjIzMTY2NzY2IiwiaWF0IjoxNTg1NTcwODYwLCJlbWFpbCI6InRjLXVzZXJAZ21haWwuY29tIiwianRpIjoiMGYxZWYxZDMtMmIzMy00OTAwLWJiNDMtNDhmMjI4NWY5NjI3In0.eBhXqSBe8zMRg2nBeGeZDgKiJdAYs0zOMzGfJCjWfcs ``` From 9b4b2247d81ddf0a53f667ecb3bd4d6ac5430a49 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 29 Jun 2020 16:23:50 +0530 Subject: [PATCH 54/93] Update joi validations to match UI needs --- src/modules/skill/service.js | 7 +++++-- src/modules/usersSkill/service.js | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js index 35010d1..4c96716 100644 --- a/src/modules/skill/service.js +++ b/src/modules/skill/service.js @@ -10,7 +10,7 @@ const methods = helper.getServiceMethods( { skillProviderId: joi.string().required(), name: joi.string().required(), - uri: joi.string().required(), + uri: joi.string(), externalId: joi.string().required() }, { @@ -19,7 +19,10 @@ const methods = helper.getServiceMethods( uri: joi.string(), externalId: joi.string() }, - {}, + { + skillProviderId: joi.string(), + externalId: joi.string() + }, async query => { const dbQueries = [] if (query.externalId) { diff --git a/src/modules/usersSkill/service.js b/src/modules/usersSkill/service.js index d8ceff7..21f113d 100644 --- a/src/modules/usersSkill/service.js +++ b/src/modules/usersSkill/service.js @@ -10,9 +10,9 @@ const methods = helper.getServiceMethods( { userId: joi.string().required(), skillId: joi.string().required(), - metricValue: joi.string().required(), - certifierId: joi.string().required(), - certifiedDate: joi.date().format('iso').required() + metricValue: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date().format('iso') }, { userId: joi.string(), From 399d488c5a44b396104c5f28250ebef72ee8e71f Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Tue, 30 Jun 2020 15:26:18 +0530 Subject: [PATCH 55/93] AWS deployment --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3936f66..e93aa45 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,9 +36,9 @@ builddeploy_steps: &builddeploy_steps 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} + ./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: From 6adf6a4f3283644556fe8051d7d21c3437199a44 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Tue, 30 Jun 2020 16:13:47 +0530 Subject: [PATCH 56/93] Update config.yml --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e93aa45..6c5ffec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,7 +63,7 @@ workflows: version: 2 build: jobs: - # Development builds are executed on "develop" branch only. + # Development builds are executed on "develop" branch only. - "build-dev": context : org-global filters: @@ -77,4 +77,4 @@ workflows: context : org-global filters: branches: - only: master \ No newline at end of file + only: master From 42f145ff5c97253aa43bf57378c1ae1b963198f7 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Tue, 30 Jun 2020 16:18:32 +0530 Subject: [PATCH 57/93] indent issue --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c5ffec..f730dff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,9 +36,9 @@ builddeploy_steps: &builddeploy_steps 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} + ./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: From f5c5ac9851901bf1179310a69cf373d8db44fd60 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Tue, 30 Jun 2020 16:21:24 +0530 Subject: [PATCH 58/93] indent issue --- build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/build.sh b/build.sh index aa24a4b..b2362f3 100755 --- a/build.sh +++ b/build.sh @@ -2,6 +2,7 @@ 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 From 0812bc7915f816c86b1ba5998f43c595e1577a38 Mon Sep 17 00:00:00 2001 From: Gunasekar-K Date: Tue, 30 Jun 2020 22:25:39 +0530 Subject: [PATCH 59/93] indent issue --- .dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 28f8ecb..fe341ce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ node_modules .idea **/.DS_Store -docs .env coverage From 865aad89c8428994b4a536206d7ed80c2ab55f9c Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 1 Jul 2020 20:00:11 +0530 Subject: [PATCH 60/93] Allow u-bahn role users to perform almost same actions as admin, except admin --- src/common/helper.js | 6 +++++- src/consts.js | 11 +++++++++-- src/modules/usersSkill/route.js | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index a5fe2e3..1ca5e49 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -221,7 +221,11 @@ function injectSearchMeta (req, res, result) { * @param recordObj the record object */ function permissionCheck (auth, recordObj) { - if (auth && auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator]) && + 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') } diff --git a/src/consts.js b/src/consts.js index 654824f..18835a2 100644 --- a/src/consts.js +++ b/src/consts.js @@ -6,13 +6,20 @@ const UserRoles = { admin: 'Admin', administrator: 'Administrator', topcoderUser: 'Topcoder User', - copilot: 'Copilot' + copilot: 'Copilot', + ubahn: 'u-bahn' } /** * all authenticated users. * @type {(string)[]} */ -const AllAuthenticatedUsers = [UserRoles.admin, UserRoles.administrator, UserRoles.topcoderUser, UserRoles.copilot] +const AllAuthenticatedUsers = [ + UserRoles.admin, + UserRoles.administrator, + UserRoles.topcoderUser, + UserRoles.copilot, + UserRoles.ubahn +] /** * all admin user diff --git a/src/modules/usersSkill/route.js b/src/modules/usersSkill/route.js index b6fd2bd..67cdc98 100644 --- a/src/modules/usersSkill/route.js +++ b/src/modules/usersSkill/route.js @@ -47,7 +47,7 @@ module.exports = { delete: { method: Controller.remove, auth: 'jwt', - access: consts.AdminUser, + access: [...consts.AdminUser, consts.UserRoles.ubahn], scopes: ['delete:usersSkill', 'all:usersSkill'] } } From a8e011f722be735caade0d063b050296f8d5ca87 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 1 Jul 2020 23:38:13 +0530 Subject: [PATCH 61/93] Update validations to support bulk processor --- src/common/es-helper.js | 4 ++++ src/modules/achievement/service.js | 6 +++--- src/modules/skill/service.js | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index f9ea4bb..c8379a9 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -109,6 +109,10 @@ const RESOURCE_FILTER = { skillProviderId: { resource: 'skill', queryField: 'skillProviderId' + }, + name: { + resource: 'skill', + queryField: 'name' } }, skillprovider: { diff --git a/src/modules/achievement/service.js b/src/modules/achievement/service.js index e868e52..e57fcaf 100644 --- a/src/modules/achievement/service.js +++ b/src/modules/achievement/service.js @@ -11,9 +11,9 @@ const methods = helper.getServiceMethods( userId: joi.string().required(), achievementsProviderId: joi.string().required(), name: joi.string().required(), - uri: joi.string().required(), - certifierId: joi.string().required(), - certifiedDate: joi.date().format('iso').required() + uri: joi.string(), + certifierId: joi.string(), + certifiedDate: joi.date().format('iso') }, { // patch request body joi schema userId: joi.string().required(), diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js index 4c96716..f5a80a9 100644 --- a/src/modules/skill/service.js +++ b/src/modules/skill/service.js @@ -21,6 +21,7 @@ const methods = helper.getServiceMethods( }, { skillProviderId: joi.string(), + name: joi.string(), externalId: joi.string() }, async query => { @@ -31,6 +32,9 @@ const methods = helper.getServiceMethods( if (query.skillProviderId) { dbQueries.push(`skillProviderId like '%${query.skillProviderId}%'`) } + if (query.name) { + dbQueries.push(`name = '${query.skillProviderId}'`) + } return dbQueries }, [['skillProviderId', 'externalId']] From e3bf845a40167ead3804b2422d621cb03e92aaeb Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 2 Jul 2020 16:04:44 +0530 Subject: [PATCH 62/93] Fix auth0 issue --- src/common/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/helper.js b/src/common/helper.js index a5fe2e3..ff4d02d 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -14,7 +14,7 @@ const { getControllerMethods, getSubControllerMethods } = require('./controller- 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_CLIENT_SECRET', 'BUSAPI_URL', 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) /** * convert json object to ion.js writer From d493bfba7f9815d7db340179e0334e3e2d26500e Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Fri, 3 Jul 2020 16:26:08 +0530 Subject: [PATCH 63/93] Correct optional property on external profile model --- src/modules/externalProfile/service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/externalProfile/service.js b/src/modules/externalProfile/service.js index 678314d..5116710 100644 --- a/src/modules/externalProfile/service.js +++ b/src/modules/externalProfile/service.js @@ -11,7 +11,7 @@ const methods = helper.getServiceMethods( userId: joi.string().required(), organizationId: joi.string().required(), externalId: joi.string().required(), - uri: joi.string().required(), + uri: joi.string(), isInactive: joi.boolean().default(false) }, { // patch request body joi schema From 68fedba8e161d8fc73003edf2ad20e284939b7de Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 8 Jul 2020 12:34:21 +0530 Subject: [PATCH 64/93] Force user to pass org details when multiple orgs are associated with user --- src/common/es-helper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index c8379a9..3e9daeb 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -278,6 +278,8 @@ async function getOrganizationId (handle) { ]) 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 } From 6f25ca3b055aaaf77d3418f76712f812db71794d Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Wed, 8 Jul 2020 12:42:23 +0530 Subject: [PATCH 65/93] For searching data in ES, pass the org id if present when constructing filters --- src/common/es-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 3e9daeb..070739c 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1018,7 +1018,7 @@ async function searchElasticSearch (resource, ...args) { const resolvedUserFilters = [] if (params.enrich && resource === 'user') { const filterKey = Object.keys(userFilters) - let authUserOrganizationId // Fetch and hold organizationId so subsequent filter resolution needn't make the same DB query to fetch it again + const authUserOrganizationId = params.organizationId for (const key of filterKey) { const resolved = await resolveUserFilterFromDb(userFilters[key], authUser, authUserOrganizationId) resolvedUserFilters.push(resolved) @@ -1173,7 +1173,7 @@ async function searchUsers (authUser, filter, params) { const userFilters = parseUserFilter(filter) const resolvedUserFilters = [] - let authUserOrganizationId + const authUserOrganizationId = filter.organizationId const filterKey = Object.keys(userFilters) for (const key of filterKey) { const resolved = await resolveUserFilterFromDb(userFilters[key], authUser, authUserOrganizationId) From b0b3d3225a6bf6917b87adadd1594ff0a9af21c5 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Fri, 10 Jul 2020 22:09:16 +0530 Subject: [PATCH 66/93] Moved clearing db to after clearing ES since clearing db could sometimes throw error about transaction limit from QLDB --- scripts/db/dropAll.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/db/dropAll.js b/scripts/db/dropAll.js index 0bee02a..cef1924 100644 --- a/scripts/db/dropAll.js +++ b/scripts/db/dropAll.js @@ -15,13 +15,13 @@ async function main () { if (models[key].tableName) { const esResourceName = modelToESIndexMapping[key] try { - await models.DBHelper.clear(models[key]) - 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`) From 13685dc683b3047b03bdfd3b2af16a41c65474f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2020 03:24:55 +0000 Subject: [PATCH 67/93] Bump lodash from 4.17.15 to 4.17.19 Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19) Signed-off-by: dependabot[bot] --- package-lock.json | 98 +++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 66 insertions(+), 34 deletions(-) mode change 100755 => 100644 package.json diff --git a/package-lock.json b/package-lock.json index 573f090..fae6e60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,9 +102,9 @@ } }, "@types/express": { - "version": "4.17.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz", - "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==", + "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": "*", @@ -122,11 +122,12 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz", - "integrity": "sha512-578YH5Lt88AKoADy0b2jQGwJtrBxezXtVe/MBqWXKZpqx91SnC0pVkVCcxcytz3lWW+cHBYDi3Ysh0WXc+rAYw==", + "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": "*" } }, @@ -139,9 +140,9 @@ } }, "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" + "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", @@ -149,9 +150,9 @@ "integrity": "sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==" }, "@types/qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==" + "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", @@ -159,9 +160,9 @@ "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, "@types/serve-static": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", - "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "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": "*" @@ -334,9 +335,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" }, "axios": { "version": "0.19.2", @@ -422,12 +423,12 @@ "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "bunyan": { - "version": "1.8.12", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", - "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "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.10.6", + "moment": "^2.19.3", "mv": "~2", "safe-json-stringify": "~1" } @@ -1406,9 +1407,9 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "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==", + "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", @@ -1986,16 +1987,16 @@ } }, "jwks-rsa": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.8.0.tgz", - "integrity": "sha512-+HYROHD5fsYQCNrJ37RSr2NjbN2/V9YT+yVF3oJxLmPIZWrmp1SOl1hMw2RcuNh+LGA2bGZIhRKGiMjhQa/b7Q==", + "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.4", - "lru-memoizer": "^2.0.1", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2", "ms": "^2.1.2" }, "dependencies": { @@ -2069,9 +2070,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "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", @@ -2697,6 +2698,11 @@ "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", @@ -2798,6 +2804,16 @@ "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", @@ -3308,6 +3324,22 @@ "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": { diff --git a/package.json b/package.json old mode 100755 new mode 100644 index 91dd2bf..ac33b31 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "http-aws-es": "^6.0.0", "ion-js": "^3.1.2", "js-yaml": "^3.13.1", - "lodash": "^4.17.15", + "lodash": "^4.17.19", "querystring": "^0.2.0", "swagger-ui-express": "^4.1.4", "tc-bus-api-wrapper": "github:topcoder-platform/tc-bus-api-wrapper", From 4776f80be40543cdca90e26fde54d46f11409b64 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Mon, 27 Jul 2020 21:23:39 +0600 Subject: [PATCH 68/93] Fixes u-bahn-app #295: unescaping URI encoded querystring and no longer filters out non-alphanumeric text from search keyword --- src/common/es-helper.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 070739c..5e9cb09 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1,5 +1,6 @@ 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') @@ -735,7 +736,7 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) { values: { terms: { field: USER_ATTRIBUTE.esDocumentValueQuery, - include: `.*${attributeValue.replace(/[A-Za-z]/g, c => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, + include: `.*${attributeValue.replace(/[^a-zA-Z]+/gi, c => `[${c}]`).replace(/[A-Za-z]/g, c => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, order: { _key: 'asc' }, @@ -1175,6 +1176,7 @@ async function searchUsers (authUser, filter, params) { 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) @@ -1257,11 +1259,11 @@ async function searchUsers (authUser, filter, params) { * @param {Object} param0 The attribute id and the attribute value properties */ async function searchAttributeValues ({ attributeId, attributeValue }) { - const esQuery = buildEsQueryToGetAttributeValues(attributeId, attributeValue, 5) + 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 From 1b8174d4110382b9dc278b83c71b549599e51373 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Mon, 27 Jul 2020 21:25:25 +0600 Subject: [PATCH 69/93] Fixes u-bahn-app #295: unescaping URI encoded querystring and no longer filters out non-alphanumeric text from search keyword --- src/common/es-helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 5e9cb09..3b6e932 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1263,7 +1263,6 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { 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 From 2853a7a6899e44b8c3598b1835804155fc8f62c8 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Date: Mon, 27 Jul 2020 22:09:51 +0600 Subject: [PATCH 70/93] Escape reserved regex characters. Fixes #295 --- src/common/es-helper.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 3b6e932..86dd667 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -706,6 +706,15 @@ function buildEsQueryFromFilter (filter) { 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 +} + /** * Build ES Query to get attribute values by attributeId * @param attributeId the attribute whose values to fetch @@ -736,7 +745,7 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) { values: { terms: { field: USER_ATTRIBUTE.esDocumentValueQuery, - include: `.*${attributeValue.replace(/[^a-zA-Z]+/gi, c => `[${c}]`).replace(/[A-Za-z]/g, c => `[${c.toLowerCase()}${c.toUpperCase()}]`)}.*`, + 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' }, @@ -1263,6 +1272,7 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { 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 From e4dc8b8d1d3433b591e020fd5d4cc246176a6a0a Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 28 Jul 2020 12:54:09 +0530 Subject: [PATCH 71/93] https://github.com/topcoder-platform/u-bahn-app/issues/295 - Fix issue where special chars in a query would not return results --- src/common/es-helper.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 86dd667..d50fb1e 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -584,6 +584,12 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } }], should: attribute.value.map(val => { + val = val + .replace(/ +/g, ' ') + .split(' ') + .map(word => word.replace(/[^a-zA-Z]/g, c => `${!isRegexReserved(c) ? c : '\\' + c}`)) + .join('* AND *') + return { query_string: { default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, @@ -711,7 +717,7 @@ function buildEsQueryFromFilter (filter) { * @param {*} char the char to check */ function isRegexReserved (char) { - const reserved = '[^$.|?*+(){}\\' + const reserved = '+-=&|!(){}[]^"~*?:\\/' return reserved.indexOf(char) !== -1 } From 68457f3e0b284153586c0b7b00b1f4b7d6cbbee7 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 28 Jul 2020 13:16:31 +0530 Subject: [PATCH 72/93] https://github.com/topcoder-platform/u-bahn-app/issues/295 - Fix issue where special chars in a query would not return results --- src/common/es-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index d50fb1e..accfb92 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -717,7 +717,7 @@ function buildEsQueryFromFilter (filter) { * @param {*} char the char to check */ function isRegexReserved (char) { - const reserved = '+-=&|!(){}[]^"~*?:\\/' + const reserved = '+-=&|!(){}[]^"~*?:\\/.$' return reserved.indexOf(char) !== -1 } From 3b927aa6053285ea5f4fabed6e4a6a0cbd551e48 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 28 Jul 2020 15:36:18 +0530 Subject: [PATCH 73/93] Revert "https://github.com/topcoder-platform/u-bahn-app/issues/295 - Fix issue where special chars in a query would not return results" This reverts commit 68457f3e0b284153586c0b7b00b1f4b7d6cbbee7 and e4dc8b8d1d3433b591e020fd5d4cc246176a6a0a --- src/common/es-helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index accfb92..d50fb1e 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -717,7 +717,7 @@ function buildEsQueryFromFilter (filter) { * @param {*} char the char to check */ function isRegexReserved (char) { - const reserved = '+-=&|!(){}[]^"~*?:\\/.$' + const reserved = '+-=&|!(){}[]^"~*?:\\/' return reserved.indexOf(char) !== -1 } From 6ba8ade5995e22723868f426ab8d11334b6646e1 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 28 Jul 2020 15:36:30 +0530 Subject: [PATCH 74/93] Revert "https://github.com/topcoder-platform/u-bahn-app/issues/295 - Fix issue where special chars in a query would not return results" This reverts commit e4dc8b8d1d3433b591e020fd5d4cc246176a6a0a. --- src/common/es-helper.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index d50fb1e..86dd667 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -584,12 +584,6 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { } }], should: attribute.value.map(val => { - val = val - .replace(/ +/g, ' ') - .split(' ') - .map(word => word.replace(/[^a-zA-Z]/g, c => `${!isRegexReserved(c) ? c : '\\' + c}`)) - .join('* AND *') - return { query_string: { default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, @@ -717,7 +711,7 @@ function buildEsQueryFromFilter (filter) { * @param {*} char the char to check */ function isRegexReserved (char) { - const reserved = '+-=&|!(){}[]^"~*?:\\/' + const reserved = '[^$.|?*+(){}\\' return reserved.indexOf(char) !== -1 } From 760c0524e83632a07d43be2a72568989316ffc0a Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Tue, 28 Jul 2020 16:38:59 +0600 Subject: [PATCH 75/93] Escape reserved regex characters when querying attribute value. Fixes #295 --- src/common/es-helper.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 86dd667..7379695 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -268,6 +268,16 @@ function getTotalCount (total) { return typeof total === 'number' ? total : total.value } +function escapeRegex(str) { + 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 +} + async function getOrganizationId (handle) { const DBHelper = require('../models/index').DBHelper @@ -573,6 +583,7 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { attribute.value = [attribute.value] } + filterClause.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, @@ -586,8 +597,8 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { should: attribute.value.map(val => { return { query_string: { - default_field: `${[USER_ATTRIBUTE.esDocumentValueStringQuery]}`, - query: `*${val.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + default_field: `${[USER_ATTRIBUTE.esDocumentValueQuery]}`, + query: `*${val.replace(/ +/g, ' ').split(' ').map(p => escapeRegex(p)).join('* AND *')}*` } } }), @@ -711,7 +722,7 @@ function buildEsQueryFromFilter (filter) { * @param {*} char the char to check */ function isRegexReserved (char) { - const reserved = '[^$.|?*+(){}\\' + const reserved = '^$#@&<>~.?+*|{}[]()"\\' return reserved.indexOf(char) !== -1 } From 5e34f0616beeea2599d2422669390e4c962574d8 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Tue, 28 Jul 2020 17:50:54 +0600 Subject: [PATCH 76/93] Fixes u-bahn-app #487 --- src/common/es-helper.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 7379695..d5f4e90 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -268,14 +268,14 @@ function getTotalCount (total) { return typeof total === 'number' ? total : total.value } -function escapeRegex(str) { - 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 +function escapeRegex (str) { + 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 } async function getOrganizationId (handle) { @@ -583,7 +583,6 @@ function setUserAttributesFiltersToEsQuery (filterClause, attributes) { attribute.value = [attribute.value] } - filterClause.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, @@ -660,7 +659,7 @@ async function setUserSearchClausesToEsQuery (boolClause, keyword) { boolClause.should.push({ query_string: { fields: ['firstName', 'lastName', 'handle'], - query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* AND *')}*` + query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* OR *')}*` } }) From ecae1e8f8f9483b4dceb53132d2589a516dccca9 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Tue, 28 Jul 2020 22:51:32 +0600 Subject: [PATCH 77/93] Fixes u-bahn-app #237 --- src/common/es-helper.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index d5f4e90..977a649 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -269,6 +269,7 @@ function getTotalCount (total) { } function escapeRegex (str) { + console.log('Escape', str) return str .replace(/[\*\+\-=~><\"\?^\${}\(\)\:\!\/[\]\\\s]/g, '\\$&') // replace single character special characters .replace(/\|\|/g, '\\||') // replace || @@ -630,7 +631,7 @@ function hasNonAlphaNumeric (text) { */ async function searchSkills (keyword) { const queryDoc = DOCUMENTS.skill - + keyword = escapeRegex(keyword) const query = hasNonAlphaNumeric(keyword) ? `\\*${keyword}\\*` : `*${keyword}*` const esQuery = { @@ -655,14 +656,14 @@ async function searchSkills (keyword) { async function setUserSearchClausesToEsQuery (boolClause, keyword) { const skillIds = await searchSkills(keyword) - boolClause.should.push({ query_string: { fields: ['firstName', 'lastName', 'handle'], - query: `*${keyword.replace(/ +/g, ' ').split(' ').join('* OR *')}*` + query: `\\*${escapeRegex(keyword.replace(/ +/g, ' ')).split(' ').join('\\* OR \\*')}\\*` } }) + keyword = escapeRegex(keyword) boolClause.should.push({ nested: { path: USER_ATTRIBUTE.esDocumentPath, From 4b602b94b5f364d83b42f3696fe1ded04b55947b Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Tue, 28 Jul 2020 22:52:38 +0600 Subject: [PATCH 78/93] Fixes u-bahn-app #237 --- src/common/es-helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 977a649..b582557 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -269,7 +269,6 @@ function getTotalCount (total) { } function escapeRegex (str) { - console.log('Escape', str) return str .replace(/[\*\+\-=~><\"\?^\${}\(\)\:\!\/[\]\\\s]/g, '\\$&') // replace single character special characters .replace(/\|\|/g, '\\||') // replace || From 0d2de5540607d181a9be2b42b094d43b79a70aa2 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 2 Aug 2020 13:22:11 +0530 Subject: [PATCH 79/93] #24 - Link skill provider with org --- docs/UBahn_API.postman_collection.json | 200 ++++++++++++++++-- docs/UBahn_ENV.postman_environment.json | 31 +-- scripts/constants.js | 3 +- .../db/data/OrganizationSkillsProvider.json | 11 + src/common/es-helper.js | 2 +- src/common/service-helper.js | 10 +- src/models/OrganizationSkillsProvider.js | 16 ++ src/models/index.js | 2 + .../organizationSkillsProvider/controller.js | 53 +++++ .../organizationSkillsProvider/route.js | 48 +++++ .../organizationSkillsProvider/service.js | 136 ++++++++++++ 11 files changed, 469 insertions(+), 43 deletions(-) create mode 100644 scripts/db/data/OrganizationSkillsProvider.json create mode 100644 src/models/OrganizationSkillsProvider.js create mode 100644 src/modules/organizationSkillsProvider/controller.js create mode 100644 src/modules/organizationSkillsProvider/route.js create mode 100644 src/modules/organizationSkillsProvider/service.js diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index 48ed820..0e5e236 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "5c139c1e-e3f2-4002-a278-e03c7b7dc97d", + "_postman_id": "66538223-0d2c-42f0-a3e0-cccc51c21b4f", "name": "UBahn_API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -14,7 +14,7 @@ { "listen": "test", "script": { - "id": "ba04f45f-dbdf-4398-9750-3eae3161dedf", + "id": "05164ddd-c806-4bad-aea0-1bfd6e43e82c", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -67,7 +67,7 @@ { "listen": "test", "script": { - "id": "64aeda82-1df8-41c7-80c9-d5cf8423fdf9", + "id": "df73daae-0b9d-4381-a261-07b1ebc7eccc", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -123,7 +123,7 @@ { "listen": "test", "script": { - "id": "e4f1d08d-a9b5-4efe-b3d9-67d3111608c1", + "id": "b37624d4-9657-4dad-9ddf-2d383a8e6d0d", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -481,7 +481,7 @@ { "listen": "test", "script": { - "id": "816c38da-3158-4d9d-828e-bb0644178a98", + "id": "dff9ccbc-34d7-47c1-9ceb-48d7472e693d", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"roleId\", rsp.id);" @@ -716,7 +716,7 @@ { "listen": "test", "script": { - "id": "7671acab-f1f8-4c29-9824-f1f2bd570028", + "id": "cce4f7a5-d16c-4bb4-bb46-e8e519f9ec11", "exec": [ "" ], @@ -958,7 +958,7 @@ { "listen": "test", "script": { - "id": "5b67a6ba-63b6-4991-a5ea-d881ea802083", + "id": "d6b690ec-4b75-4a8f-9099-16464365f51f", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"organizationId\", rsp.id);" @@ -1193,7 +1193,7 @@ { "listen": "test", "script": { - "id": "88d7b18f-c5e1-4577-9d03-a93d9f9d5c7e", + "id": "e401a38a-b4cb-4b5b-8fdd-24e91249f6ff", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillsProviderId\", rsp.id);" @@ -1428,7 +1428,7 @@ { "listen": "test", "script": { - "id": "92b83849-961d-4ad3-bac2-d7f457e5fbd7", + "id": "a4db0fe1-638f-4d8d-81c8-08967498d1de", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillId\", rsp.id);" @@ -1664,7 +1664,7 @@ { "listen": "test", "script": { - "id": "d5c59be3-63df-485b-a889-aa4d00c90571", + "id": "95226f08-c8a4-4915-91bb-8018ba933ac3", "exec": [ "" ], @@ -1912,7 +1912,7 @@ { "listen": "test", "script": { - "id": "d8143ed1-b600-4ef3-845c-396ed909e821", + "id": "63cd9db1-0910-4834-a3a6-519a8e2bea9f", "exec": [ "" ], @@ -2160,7 +2160,7 @@ { "listen": "test", "script": { - "id": "6c13436d-c2ee-4867-98ac-eb513e9edb3e", + "id": "b6c6134f-a0af-4f0e-b5b2-90c6c2409491", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"achievementsProviderId\", rsp.id);" @@ -2395,7 +2395,7 @@ { "listen": "test", "script": { - "id": "4453dd7e-65fc-47c0-9081-7a78a6ed1446", + "id": "33ed1f80-a4b5-4984-89b4-4c53836b9966", "exec": [ "" ], @@ -2644,7 +2644,7 @@ { "listen": "test", "script": { - "id": "d34d4953-a1f9-41ef-a9dd-6fa7c9431c3f", + "id": "a0d7915e-0e12-4108-bf56-5d4767c03974", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeGroupId\", rsp.id);" @@ -2885,7 +2885,7 @@ { "listen": "test", "script": { - "id": "71539226-1b42-4aa3-9f51-d5b488c817d2", + "id": "d6d57cb8-63a2-4778-b1e2-4dbc361aee0f", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeId\", rsp.id);" @@ -3125,7 +3125,7 @@ { "listen": "test", "script": { - "id": "5c987711-36d9-4472-b6d0-db4edc47685e", + "id": "0da60ea8-92b2-43e4-985a-4a507b26b59a", "exec": [ "" ], @@ -3404,6 +3404,174 @@ } ], "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": {} diff --git a/docs/UBahn_ENV.postman_environment.json b/docs/UBahn_ENV.postman_environment.json index 6ae77ae..6645a14 100644 --- a/docs/UBahn_ENV.postman_environment.json +++ b/docs/UBahn_ENV.postman_environment.json @@ -1,20 +1,20 @@ { - "id": "5ceec1fa-02fe-4556-be97-40e279ad003a", + "id": "1f07c3cc-5af9-4dc1-b038-129aaf1fac6e", "name": "UBahn_ENV", "values": [ { "key": "HOST", - "value": "http://127.0.0.1:3001/api/1.0", + "value": "http://127.0.0.1:3002/api/1.0", "enabled": true }, { "key": "token", - "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLXVzZXIiLCJleHAiOjE2ODU1NzE0NjAsInVzZXJJZCI6IjIzMTY2NzY2IiwiaWF0IjoxNTg1NTcwODYwLCJlbWFpbCI6InRjLXVzZXJAZ21haWwuY29tIiwianRpIjoiMGYxZWYxZDMtMmIzMy00OTAwLWJiNDMtNDhmMjI4NWY5NjI3In0.eBhXqSBe8zMRg2nBeGeZDgKiJdAYs0zOMzGfJCjWfcs", + "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlcyI6WyJUb3Bjb2RlciBVc2VyIiwiQ29waWxvdCIsIkFkbWluIl0sImlzcyI6Imh0dHBzOi8vYXBpLnRvcGNvZGVyLmNvbSIsImhhbmRsZSI6InRjLUFkbWluIiwiZXhwIjoxNjg1NTcxNDYwLCJ1c2VySWQiOiIyMzE2Njc2OCIsImlhdCI6MTU4NTU3MDg2MCwiZW1haWwiOiJ0Yy1BZG1pbkBnbWFpbC5jb20iLCJqdGkiOiIwZjFlZjFkMy0yYjMzLTQ5MDAtYmI0My00OGYyMjg1Zjk2MzAifQ.D_TtClF4xkuSPSWoUYvkWigUWVFhH5UuF7Eci4S1_xg", "enabled": true }, { "key": "userId", - "value": "ce348067-e73f-49d7-af72-fcf11a6c88bf", + "value": "55f6fa1c-fc38-4b74-83ad-278babd7efd2", "enabled": true }, { @@ -24,36 +24,21 @@ }, { "key": "skillsProviderId", - "value": "", + "value": "7637ae1a-3b7c-44eb-a5ed-10ea02f1885d", "enabled": true }, { "key": "skillId", - "value": "", + "value": "e72e432f-6ef4-4f19-962b-eb59b805b317", "enabled": true }, { "key": "organizationId", "value": "36ed815b-3da1-49f1-a043-aaed0a4e81ad", "enabled": true - }, - { - "key": "achievementsProviderId", - "value": "eb327c00-0090-45af-96d2-593408c96397", - "enabled": true - }, - { - "key": "attributeGroupId", - "value": "84634bbd-8191-40cf-a03e-9962d7e39fda", - "enabled": true - }, - { - "key": "attributeId", - "value": "f3fd623f-a613-4e3c-bf2f-9df529ff4317", - "enabled": true } ], "_postman_variable_scope": "environment", - "_postman_exported_at": "2020-05-13T08:36:10.472Z", - "_postman_exported_using": "Postman/7.24.0" + "_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/scripts/constants.js b/scripts/constants.js index 8ac3d04..17baf6c 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -77,7 +77,8 @@ const modelToESIndexMapping = { AchievementsProvider: 'achievementprovider', AttributeGroup: 'attributegroup', Attribute: 'attribute', - UserAttribute: 'userattribute' + UserAttribute: 'userattribute', + OrganizationSkillsProvider: 'organizationskillprovider' } module.exports = { 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/src/common/es-helper.js b/src/common/es-helper.js index b582557..c8c7091 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -197,7 +197,7 @@ const RESOURCE_FILTER = { } } -// filter chaim config +// filter chain config const FILTER_CHAIN = { user: { idField: 'id' diff --git a/src/common/service-helper.js b/src/common/service-helper.js index 57f58b3..a3b85e5 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -27,7 +27,8 @@ const MODEL_TO_RESOURCE = { SkillsProvider: 'skillprovider', AchievementsProvider: 'achievementprovider', UsersAttribute: 'userattribute', - UsersRole: 'userrole' + UsersRole: 'userrole', + OrganizationSkillsProvider: 'organizationskillprovider' } /** @@ -312,5 +313,10 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil } module.exports = { - getServiceMethods + getServiceMethods, + makeSureRefExist, + makeSureUnique, + getResource, + buildQueryByParams, + publishMessage } 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/index.js b/src/models/index.js index a7b4c47..18b65ef 100755 --- a/src/models/index.js +++ b/src/models/index.js @@ -15,6 +15,7 @@ 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') @@ -34,6 +35,7 @@ module.exports = { AttributeGroup, Attribute, UserAttribute, + OrganizationSkillsProvider, consts, DBHelper } diff --git a/src/modules/organizationSkillsProvider/controller.js b/src/modules/organizationSkillsProvider/controller.js new file mode 100644 index 0000000..6a4243b --- /dev/null +++ b/src/modules/organizationSkillsProvider/controller.js @@ -0,0 +1,53 @@ +/** + * the organization skills provider controller + */ + +const service = require('./service') +const _ = require('lodash') +const { injectSearchMeta } = require('../../common/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)) +} + +/** + * 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.organizationId, req.params.skillProviderId, req.auth)) +} + +/** + * 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.organizationId, req.params.skillProviderId, req.auth) + res.status(204).end() +} + +module.exports = { + create, + search, + remove, + get +} 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..9739382 --- /dev/null +++ b/src/modules/organizationSkillsProvider/service.js @@ -0,0 +1,136 @@ +/** + * the organization skills provider services + */ + +const joi = require('@hapi/joi') +const _ = require('lodash') +const models = require('../../models/index') +const helper = require('../../common/helper') +const serviceHelper = require('../../common/service-helper') +const logger = require('../../common/logger') +const errors = require('../../common/errors') +const appConst = require('../../consts') +const resource = serviceHelper.getResource(models.OrganizationSkillsProvider.name) + +/** + * create entity + * @param entity the organizationskillprovider details + * @param auth the auth information + * @return {Promise} the created organizationSkillProvider + */ +async function create (entity, auth) { + await serviceHelper.makeSureUnique(models.OrganizationSkillsProvider, entity, [['organizationId', 'skillProviderId']]) + await serviceHelper.makeSureRefExist(entity) + + const dbEntity = new models.OrganizationSkillsProvider() + _.extend(dbEntity, entity) + dbEntity.created = new Date() + dbEntity.createdBy = helper.getAuthUser(auth) + await models.DBHelper.save(models.OrganizationSkillsProvider, dbEntity) + try { + await serviceHelper.publishMessage('create', resource, dbEntity) + } catch (err) { + logger.logFullError(err) + } + return dbEntity +} + +create.schema = { + entity: { + organizationId: joi.string().required(), + skillProviderId: joi.string().required() + }, + auth: joi.object() +} + +/** + * get organizationskillprovider by org and skill provider id(s) + * @param organizationId the org id + * @param skillProviderId the skill provider id + * @param auth the auth obj + * @return {Promise} the db organizationskillprovider + */ +async function get (organizationId, skillProviderId, auth) { + const payload = { organizationId, skillProviderId } + // TODO - First query ES + // 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 (err.status && err.status === 403) { + // throw errors.elasticSearchEnrichError(err.message) + // } + // logger.logFullError(err) + // } + const items = await models.DBHelper.find( + models.OrganizationSkillsProvider, + serviceHelper.buildQueryByParams(payload) + ) + const recordObj = items[0] + if (!recordObj) { + throw errors.newEntityNotFoundError(`cannot find ${models.OrganizationSkillsProvider.tableName} where ${_.map(payload, (v, k) => `${k}:${v}`).join(', ')}`) + } + helper.permissionCheck(auth, recordObj) + return recordObj +} + +/** + * search organizationskillproviders by query + * @param query the search query + * @param auth the auth object + * @return {Promise} the results + */ +async function search (query, auth) { + // TODO - First query ES + // try { + // return await esHelper.searchElasticSearch(resource, query, auth) + // } catch (err) { + // logger.logFullError(err) + // } + + // Elasticsearch failed. Hit the database + const dbQueries = [`organizationId = '${query.organizationId}'`] + + // user token + // for non-admin users, this endpoint will only return entities that the user has created. + if (auth.roles && !helper.checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { + dbQueries.push(`${models.OrganizationSkillsProvider.tableName}.createdBy = '${helper.getAuthUser(auth)}'`) + } + const items = await models.DBHelper.find(models.OrganizationSkillsProvider, 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: { + organizationId: joi.string().required() + }, + auth: joi.object() +} + +/** + * remove entity by id + * @param organizationId the org id + * @param skillProviderId the skill provider id + * @param auth the auth obj + * @return {Promise} no data returned + */ +async function remove (organizationId, skillProviderId, auth) { + await get(organizationId, skillProviderId, auth) // check exist + const payload = { organizationId, skillProviderId } + await models.DBHelper.delete(models.OrganizationSkillsProvider, null, serviceHelper.buildQueryByParams(payload)) + + try { + await serviceHelper.publishMessage('remove', resource, { payload }) + } catch (err) { + logger.logFullError(err) + } +} + +module.exports = { + create, search, get, remove +} From 5e38832e4d1892998ea00e57cb7538be14b1a195 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 2 Aug 2020 17:56:29 +0530 Subject: [PATCH 80/93] #24 - Scrap individual service and controller for org skill provider and instead use generic one AND integrate with ES --- config/default.js | 4 + scripts/constants.js | 12 +- scripts/db/genData.js | 34 ++++- src/common/es-helper.js | 144 ++++++++++++++---- src/common/service-helper.js | 16 +- src/models/index.js | 27 ++-- .../organizationSkillsProvider/controller.js | 48 +----- .../organizationSkillsProvider/service.js | 132 ++-------------- 8 files changed, 195 insertions(+), 222 deletions(-) diff --git a/config/default.js b/config/default.js index 46be717..f88ffea 100755 --- a/config/default.js +++ b/config/default.js @@ -89,6 +89,10 @@ module.exports = { }, 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/scripts/constants.js b/scripts/constants.js index 17baf6c..755b650 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -64,6 +64,13 @@ const userResources = { } } +const organizationResources = { + organizationskillprovider: { + propertyName: config.get('ES.DOCUMENTS.organizationskillprovider.orgField'), + relateKey: 'skillProviderId' + } +} + const modelToESIndexMapping = { User: 'user', Role: 'role', @@ -82,5 +89,8 @@ const modelToESIndexMapping = { } module.exports = { - topResources, userResources, modelToESIndexMapping + topResources, + userResources, + organizationResources, + modelToESIndexMapping } diff --git a/scripts/db/genData.js b/scripts/db/genData.js index fea3401..2f501a1 100644 --- a/scripts/db/genData.js +++ b/scripts/db/genData.js @@ -2,7 +2,12 @@ const _ = require('lodash') const models = require('../../src/models') const logger = require('../../src/common/logger') const { getESClient } = require('../../src/common/es-client') -const { topResources, userResources, modelToESIndexMapping } = require('../constants') +const { + topResources, + userResources, + organizationResources, + modelToESIndexMapping +} = require('../constants') async function insertIntoES (modelName, body) { const esResourceName = modelToESIndexMapping[modelName] @@ -66,6 +71,33 @@ async function insertIntoES (modelName, body) { 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' + }) + } } } diff --git a/src/common/es-helper.js b/src/common/es-helper.js index c8c7091..de30a10 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -9,12 +9,17 @@ const esClient = require('./es-client').getESClient() const DOCUMENTS = config.ES.DOCUMENTS const RESOURCES = Object.keys(DOCUMENTS) -const SUB_DOCUMENTS = {} -const SUB_PROPERTIES = [] +const SUB_USER_DOCUMENTS = {} +const SUB_ORG_DOCUMENTS = {} +const SUB_USER_PROPERTIES = [] +const SUB_ORG_PROPERTIES = [] _.forOwn(DOCUMENTS, (value, key) => { if (value.userField) { - SUB_DOCUMENTS[key] = value - SUB_PROPERTIES.push(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) } }) @@ -241,7 +246,7 @@ const FILTER_CHAIN = { }, // sub resource userskill: { - queryFielid: 'skillId', + queryField: 'skillId', enrichNext: 'skill', idField: 'skillId' }, @@ -261,6 +266,11 @@ const FILTER_CHAIN = { userattribute: { enrichNext: 'attribute', idField: 'attributeId' + }, + organizationskillprovider: { + queryField: 'skillProviderId', + enrichNext: 'skillprovider', + idField: 'skillProviderId' } } @@ -269,6 +279,7 @@ function getTotalCount (total) { } function escapeRegex (str) { + /* eslint-disable no-useless-escape */ return str .replace(/[\*\+\-=~><\"\?^\${}\(\)\:\!\/[\]\\\s]/g, '\\$&') // replace single character special characters .replace(/\|\|/g, '\\||') // replace || @@ -276,6 +287,7 @@ function escapeRegex (str) { .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) { @@ -398,8 +410,8 @@ async function enrichResource (resource, enrichIdProp, item) { * @returns {Promise<*>} the promise of enriched user */ async function enrichUser (user) { - for (const subProp of Object.keys(SUB_DOCUMENTS)) { - const subDoc = SUB_DOCUMENTS[subProp] + 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) { @@ -440,23 +452,43 @@ async function getFromElasticSearch (resource, ...args) { const doc = DOCUMENTS[resource] const userDoc = DOCUMENTS.user - const subDoc = SUB_DOCUMENTS[resource] + 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 - const esQuery = { - index: doc.userField ? userDoc.index : doc.index, - type: doc.userField ? userDoc.type : doc.type, - id: doc.userField ? params.userId : id + 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_PROPERTIES.join(',') + esQuery._source_excludes = SUB_USER_PROPERTIES.join(',') } - } else if (subDoc) { - esQuery._source_includes = subDoc.userField + } 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)}`) @@ -469,14 +501,22 @@ async function getFromElasticSearch (resource, ...args) { const groups = await groupApi.getGroups(user.id) user.groups = groups return user - } else if (subDoc) { + } else if (subUserDoc) { // find top sub doc by sub.id - const found = result[subDoc.userField].find(sub => sub[filterChain.idField] === params[filterChain.idField]) + 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 } @@ -527,7 +567,14 @@ function setResourceFilterToEsQuery (resFilters, esQuery) { if (resFilters.length > 0) { for (const filter of resFilters) { const doc = DOCUMENTS[filter.resource] - let matchField = doc.userField ? `${doc.userField}.${filter.queryField}` : `${filter.queryField}` + 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' } @@ -917,11 +964,20 @@ async function resolveResFilter (filter, initialRes) { // return the value if this is end of the filter if (filter.resource === initialRes || !filterChain.filterNext) { - return { - resource: filter.resource, - userField: doc.userField, - queryField: filter.queryField, - value: filter.value + 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 + } } } @@ -998,7 +1054,9 @@ async function searchElasticSearch (resource, ...args) { const authUser = args[1] const doc = DOCUMENTS[resource] const userDoc = DOCUMENTS.user - const topSubDoc = SUB_DOCUMENTS[resource] + const orgDoc = DOCUMENTS.organization + const topUserSubDoc = SUB_USER_DOCUMENTS[resource] + const topOrgSubDoc = SUB_ORG_DOCUMENTS[resource] if (!params.page) { params.page = 1 } @@ -1051,8 +1109,8 @@ async function searchElasticSearch (resource, ...args) { // construct ES query const esQuery = { - index: doc.userField ? userDoc.index : doc.index, - type: doc.userField ? userDoc.type : doc.type, + 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: { @@ -1104,9 +1162,9 @@ async function searchElasticSearch (resource, ...args) { }) } else { // do not return sub-resources - esQuery._source_excludes = SUB_PROPERTIES.join(',') + esQuery._source_excludes = SUB_USER_PROPERTIES.join(',') } - } else if (topSubDoc) { + } else if (topUserSubDoc) { // add userId match const userFC = FILTER_CHAIN.user const userIdMatchField = `${userFC.idField}.keyword` @@ -1115,13 +1173,30 @@ async function searchElasticSearch (resource, ...args) { [userIdMatchField]: params.userId } }) - esQuery._source_includes = topSubDoc.userField + 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) { - const matchField = filter.userField ? `${filter.userField}.${filter.queryField}` : `${filter.queryField}` + 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) } } @@ -1152,11 +1227,16 @@ async function searchElasticSearch (resource, ...args) { const groups = await groupApi.getGroups(user.id) user.groups = groups } - } else if (topSubDoc) { - result = docs.hits.hits[0]._source[topSubDoc.userField] + } 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) } diff --git a/src/common/service-helper.js b/src/common/service-helper.js index a3b85e5..8dcbe8a 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -14,10 +14,13 @@ const OP_TO_TOPIC = { } // Used for determining the payload structure when posting to bus api -const SUB_DOCUMENTS = {} +const SUB_USER_DOCUMENTS = {} +const SUB_ORG_DOCUMENTS = {} _.forOwn(config.ES.DOCUMENTS, (value, key) => { if (value.userField) { - SUB_DOCUMENTS[key] = value + SUB_USER_DOCUMENTS[key] = value + } else if (value.orgField) { + SUB_ORG_DOCUMENTS[key] = value } }) @@ -293,7 +296,7 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil let payload await get(id, auth, params) // check exist await models.DBHelper.delete(Model, id, buildQueryByParams(params)) - if (SUB_DOCUMENTS[resource]) { + if (SUB_USER_DOCUMENTS[resource] || SUB_ORG_DOCUMENTS[resource]) { payload = _.assign({}, params) } else { payload = { @@ -313,10 +316,5 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil } module.exports = { - getServiceMethods, - makeSureRefExist, - makeSureUnique, - getResource, - buildQueryByParams, - publishMessage + getServiceMethods } diff --git a/src/models/index.js b/src/models/index.js index 18b65ef..e663cff 100755 --- a/src/models/index.js +++ b/src/models/index.js @@ -44,17 +44,18 @@ module.exports = { */ 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(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/organizationSkillsProvider/controller.js b/src/modules/organizationSkillsProvider/controller.js index 6a4243b..7b2625b 100644 --- a/src/modules/organizationSkillsProvider/controller.js +++ b/src/modules/organizationSkillsProvider/controller.js @@ -3,51 +3,9 @@ */ const service = require('./service') -const _ = require('lodash') -const { injectSearchMeta } = require('../../common/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)) -} - -/** - * 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.organizationId, req.params.skillProviderId, req.auth)) -} - -/** - * 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.organizationId, req.params.skillProviderId, req.auth) - res.status(204).end() -} +const helper = require('../../common/helper') +const methods = helper.getSubControllerMethods(service) module.exports = { - create, - search, - remove, - get + ...methods } diff --git a/src/modules/organizationSkillsProvider/service.js b/src/modules/organizationSkillsProvider/service.js index 9739382..2a64c68 100644 --- a/src/modules/organizationSkillsProvider/service.js +++ b/src/modules/organizationSkillsProvider/service.js @@ -3,134 +3,24 @@ */ const joi = require('@hapi/joi') -const _ = require('lodash') const models = require('../../models/index') const helper = require('../../common/helper') -const serviceHelper = require('../../common/service-helper') -const logger = require('../../common/logger') -const errors = require('../../common/errors') -const appConst = require('../../consts') -const resource = serviceHelper.getResource(models.OrganizationSkillsProvider.name) - -/** - * create entity - * @param entity the organizationskillprovider details - * @param auth the auth information - * @return {Promise} the created organizationSkillProvider - */ -async function create (entity, auth) { - await serviceHelper.makeSureUnique(models.OrganizationSkillsProvider, entity, [['organizationId', 'skillProviderId']]) - await serviceHelper.makeSureRefExist(entity) - - const dbEntity = new models.OrganizationSkillsProvider() - _.extend(dbEntity, entity) - dbEntity.created = new Date() - dbEntity.createdBy = helper.getAuthUser(auth) - await models.DBHelper.save(models.OrganizationSkillsProvider, dbEntity) - try { - await serviceHelper.publishMessage('create', resource, dbEntity) - } catch (err) { - logger.logFullError(err) - } - return dbEntity -} - -create.schema = { - entity: { +const methods = helper.getServiceMethods( + models.OrganizationSkillsProvider, + { organizationId: joi.string().required(), skillProviderId: joi.string().required() }, - auth: joi.object() -} - -/** - * get organizationskillprovider by org and skill provider id(s) - * @param organizationId the org id - * @param skillProviderId the skill provider id - * @param auth the auth obj - * @return {Promise} the db organizationskillprovider - */ -async function get (organizationId, skillProviderId, auth) { - const payload = { organizationId, skillProviderId } - // TODO - First query ES - // 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 (err.status && err.status === 403) { - // throw errors.elasticSearchEnrichError(err.message) - // } - // logger.logFullError(err) - // } - const items = await models.DBHelper.find( - models.OrganizationSkillsProvider, - serviceHelper.buildQueryByParams(payload) - ) - const recordObj = items[0] - if (!recordObj) { - throw errors.newEntityNotFoundError(`cannot find ${models.OrganizationSkillsProvider.tableName} where ${_.map(payload, (v, k) => `${k}:${v}`).join(', ')}`) - } - helper.permissionCheck(auth, recordObj) - return recordObj -} - -/** - * search organizationskillproviders by query - * @param query the search query - * @param auth the auth object - * @return {Promise} the results - */ -async function search (query, auth) { - // TODO - First query ES - // try { - // return await esHelper.searchElasticSearch(resource, query, auth) - // } catch (err) { - // logger.logFullError(err) - // } - - // Elasticsearch failed. Hit the database - const dbQueries = [`organizationId = '${query.organizationId}'`] - - // user token - // for non-admin users, this endpoint will only return entities that the user has created. - if (auth.roles && !helper.checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { - dbQueries.push(`${models.OrganizationSkillsProvider.tableName}.createdBy = '${helper.getAuthUser(auth)}'`) - } - const items = await models.DBHelper.find(models.OrganizationSkillsProvider, 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: { + { + organizationId: joi.string(), + skillProviderId: joi.string() + }, + { organizationId: joi.string().required() }, - auth: joi.object() -} - -/** - * remove entity by id - * @param organizationId the org id - * @param skillProviderId the skill provider id - * @param auth the auth obj - * @return {Promise} no data returned - */ -async function remove (organizationId, skillProviderId, auth) { - await get(organizationId, skillProviderId, auth) // check exist - const payload = { organizationId, skillProviderId } - await models.DBHelper.delete(models.OrganizationSkillsProvider, null, serviceHelper.buildQueryByParams(payload)) - - try { - await serviceHelper.publishMessage('remove', resource, { payload }) - } catch (err) { - logger.logFullError(err) - } -} + async (query) => [`organizationId = '${query.organizationId}'`], + [['organizationId', 'skillProviderId']]) module.exports = { - create, search, get, remove + ...methods } From 778d20cc1d370407291d07582d2e94b5af2d74ba Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Sun, 2 Aug 2020 19:48:34 +0530 Subject: [PATCH 81/93] #24 - uncomment table creation --- src/models/index.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/models/index.js b/src/models/index.js index e663cff..8eecacc 100755 --- a/src/models/index.js +++ b/src/models/index.js @@ -44,18 +44,18 @@ module.exports = { */ 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) + 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) } From 626a1dcbe583d968fae8edb1324c2bb3803a3cb5 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Sun, 2 Aug 2020 22:30:01 +0600 Subject: [PATCH 82/93] Search achievements by organization ID and keyword --- docs/UBahn_API.postman_collection.json | 99 +++++++++++++++++++++----- src/common/es-helper.js | 46 +++++++++++- src/modules/search/controller.js | 11 ++- src/modules/search/route.js | 8 +++ 4 files changed, 143 insertions(+), 21 deletions(-) diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index 0e5e236..a86efbb 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "66538223-0d2c-42f0-a3e0-cccc51c21b4f", + "_postman_id": "2e9d0d1e-22b4-4b5c-af7d-8e5d407de79b", "name": "UBahn_API", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -14,7 +14,7 @@ { "listen": "test", "script": { - "id": "05164ddd-c806-4bad-aea0-1bfd6e43e82c", + "id": "256b27ab-e629-429f-ad93-372eaaa46239", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -61,13 +61,65 @@ }, "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/users", "event": [ { "listen": "test", "script": { - "id": "df73daae-0b9d-4381-a261-07b1ebc7eccc", + "id": "75560f72-efeb-438e-ac29-24d7c6ab540e", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -123,7 +175,7 @@ { "listen": "test", "script": { - "id": "b37624d4-9657-4dad-9ddf-2d383a8e6d0d", + "id": "5db9dc16-daad-4768-a3af-ea8983487bec", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"userId\", rsp.id);" @@ -149,7 +201,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"handle\":\"handle_01\"\n}" + "raw": "{\n\t\"handle\":\"handle_01\"\n}", + "options": { + "raw": {} + } }, "url": { "raw": "{{HOST}}/users", @@ -212,7 +267,10 @@ ], "body": { "mode": "raw", - "raw": "{\n\t\"handle\":\"handle_05\"\n}" + "raw": "{\n\t\"handle\":\"handle_05\"\n}", + "options": { + "raw": {} + } }, "url": { "raw": "{{HOST}}/users/{{userId}}", @@ -454,7 +512,10 @@ ], "body": { "mode": "raw", - "raw": "" + "raw": "", + "options": { + "raw": {} + } }, "url": { "raw": "{{HOST}}/users/{{userId}}", @@ -481,7 +542,7 @@ { "listen": "test", "script": { - "id": "dff9ccbc-34d7-47c1-9ceb-48d7472e693d", + "id": "8469c20d-3ef5-4868-af50-d603584ed8a7", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"roleId\", rsp.id);" @@ -716,7 +777,7 @@ { "listen": "test", "script": { - "id": "cce4f7a5-d16c-4bb4-bb46-e8e519f9ec11", + "id": "d4ca59b9-f350-4c91-9667-a5e749eed956", "exec": [ "" ], @@ -958,7 +1019,7 @@ { "listen": "test", "script": { - "id": "d6b690ec-4b75-4a8f-9099-16464365f51f", + "id": "ff10985c-75c8-48ba-b522-2c8213b0c6cd", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"organizationId\", rsp.id);" @@ -1193,7 +1254,7 @@ { "listen": "test", "script": { - "id": "e401a38a-b4cb-4b5b-8fdd-24e91249f6ff", + "id": "0a875ba8-b4dc-42cc-b8ba-deb560b73556", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillsProviderId\", rsp.id);" @@ -1428,7 +1489,7 @@ { "listen": "test", "script": { - "id": "a4db0fe1-638f-4d8d-81c8-08967498d1de", + "id": "909e2488-3801-4b57-90c5-6a6ae2e91f13", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"skillId\", rsp.id);" @@ -1664,7 +1725,7 @@ { "listen": "test", "script": { - "id": "95226f08-c8a4-4915-91bb-8018ba933ac3", + "id": "e3241acb-646f-4ec8-83fb-8a3d63da4990", "exec": [ "" ], @@ -1912,7 +1973,7 @@ { "listen": "test", "script": { - "id": "63cd9db1-0910-4834-a3a6-519a8e2bea9f", + "id": "ee9256b0-cdc2-4194-9da7-c0b363082385", "exec": [ "" ], @@ -2160,7 +2221,7 @@ { "listen": "test", "script": { - "id": "b6c6134f-a0af-4f0e-b5b2-90c6c2409491", + "id": "3c959e64-93e9-480d-b92e-44eacb46c2b1", "exec": [ "var rsp = pm.response.json();", "if(rsp.id) pm.environment.set(\"achievementsProviderId\", rsp.id);" @@ -2395,7 +2456,7 @@ { "listen": "test", "script": { - "id": "33ed1f80-a4b5-4984-89b4-4c53836b9966", + "id": "9b4f70b8-3ca8-4c1c-a65a-8a8b37e2fa62", "exec": [ "" ], @@ -2644,7 +2705,7 @@ { "listen": "test", "script": { - "id": "a0d7915e-0e12-4108-bf56-5d4767c03974", + "id": "fb796928-9848-47a3-be88-61a8fc9e60f7", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeGroupId\", rsp.id);" @@ -2885,7 +2946,7 @@ { "listen": "test", "script": { - "id": "d6d57cb8-63a2-4778-b1e2-4dbc361aee0f", + "id": "eb028137-ee03-496c-a812-2966380bca42", "exec": [ "var rsp = pm.response.json();", "if (rsp.id) pm.environment.set(\"attributeId\", rsp.id);" @@ -3125,7 +3186,7 @@ { "listen": "test", "script": { - "id": "0da60ea8-92b2-43e4-985a-4a507b26b59a", + "id": "06c18b73-b9e6-46a8-acfa-74fc7ae68ac5", "exec": [ "" ], diff --git a/src/common/es-helper.js b/src/common/es-helper.js index de30a10..37e4075 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -772,6 +772,36 @@ function isRegexReserved (char) { 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 + } + } + } + } + } + + 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 @@ -1382,9 +1412,23 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { return { result } } +async function searchAchievementValues ({ organizationId, keyword }) { + 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 => ({ name: a.key })) + + return { + result + } +} + module.exports = { searchElasticSearch, getFromElasticSearch, searchUsers, - searchAttributeValues + searchAttributeValues, + searchAchievementValues } diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js index 421ca23..da40ca7 100644 --- a/src/modules/search/controller.js +++ b/src/modules/search/controller.js @@ -21,7 +21,16 @@ async function searchAttributeValues (req, res) { 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, - searchAttributeValues + searchAttributeValues, + searchAchievementValues } diff --git a/src/modules/search/route.js b/src/modules/search/route.js index 69d5c3e..6fa3899 100644 --- a/src/modules/search/route.js +++ b/src/modules/search/route.js @@ -21,5 +21,13 @@ module.exports = { access: consts.AdminUser, scopes: ['create:userAttribute', 'all:userAttribute'] } + }, + '/search/userAchievements': { + get: { + method: Controller.searchAchievementValues, + auth: 'jwt', + access: consts.AdminUser, + scopes: ['create:userAttribute', 'all:userAttribute'] + } } } From 56e0ef7939820306facfda35adad676d181bd55f Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Sun, 2 Aug 2020 23:20:21 +0600 Subject: [PATCH 83/93] Include achievementId in response --- src/common/es-helper.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 37e4075..09c7f46 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -52,6 +52,7 @@ const USER_FILTER_TO_MODEL = { queryField: 'name', esDocumentValueQuery: 'achievements.name', esDocumentQuery: 'achievements.id.keyword', + esDocumentId: 'achievements.id', values: [] }, get achievements () { return this.achievement }, @@ -791,6 +792,13 @@ function buildEsQueryToGetAchievements (organizationId, keyword, size) { 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] + } + } } } } @@ -1418,7 +1426,21 @@ async function searchAchievementValues ({ organizationId, keyword }) { const esResult = await esClient.search(esQuery) logger.debug(`ES response ${JSON.stringify(esResult, null, 2)}`) - const result = esResult.aggregations.achievements.buckets.map(a => ({ name: a.key })) + const result = esResult.aggregations.achievements.buckets.map(a => { + let achievementName = a.key + let achievementId = null + + for (let achievement of a.ids.hits.hits[0]._source.achievements) { + if (achievement.name == achievementName) { + achievementId = achievement.id + break; + } + } + return { + id: achievementId, + name: achievementName + }; + }) return { result From 8d21badd55c28ebc14527d012bd27fecec6f6140 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Mon, 3 Aug 2020 17:19:51 +0600 Subject: [PATCH 84/93] API to searchs kills associated with an organization --- docs/UBahn_API.postman_collection.json | 52 ++++++++++++ src/common/es-helper.js | 107 ++++++++++++++++++++----- src/modules/search/controller.js | 9 +++ src/modules/search/route.js | 8 ++ 4 files changed, 157 insertions(+), 19 deletions(-) diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index a86efbb..683d90b 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -113,6 +113,58 @@ }, "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/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/users", "event": [ diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 09c7f46..a4474f4 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -676,33 +676,53 @@ function hasNonAlphaNumeric (text) { * @param keyword the search keyword * @returns array of skillIds */ -async function searchSkills (keyword) { +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: { - query: { - query_string: { - default_field: 'name', - minimum_should_match: '100%', - query - } - }, - _source: 'id' - } + 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.id) + + return results.hits.hits.map(hit => hit._source) } async function setUserSearchClausesToEsQuery (boolClause, keyword) { - const skillIds = await searchSkills(keyword) + const skillIds = (await searchSkills(keyword)).map(skill => skill.id) boolClause.should.push({ query_string: { fields: ['firstName', 'lastName', 'handle'], @@ -866,6 +886,27 @@ function buildEsQueryToGetAttributeValues (attributeId, attributeValue, size) { 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 @@ -1391,6 +1432,33 @@ async function searchUsers (authUser, filter, params) { } } +/** + * 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 }) { + 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.id))) + 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 @@ -1427,19 +1495,19 @@ async function searchAchievementValues ({ organizationId, keyword }) { const esResult = await esClient.search(esQuery) logger.debug(`ES response ${JSON.stringify(esResult, null, 2)}`) const result = esResult.aggregations.achievements.buckets.map(a => { - let achievementName = a.key + const achievementName = a.key let achievementId = null - - for (let achievement of a.ids.hits.hits[0]._source.achievements) { - if (achievement.name == achievementName) { + + for (const achievement of a.ids.hits.hits[0]._source.achievements) { + if (achievement.name === achievementName) { achievementId = achievement.id - break; + break } } return { id: achievementId, name: achievementName - }; + } }) return { @@ -1451,6 +1519,7 @@ module.exports = { searchElasticSearch, getFromElasticSearch, searchUsers, + searchSkillsInOrganization, searchAttributeValues, searchAchievementValues } diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js index da40ca7..b29bfcf 100644 --- a/src/modules/search/controller.js +++ b/src/modules/search/controller.js @@ -13,6 +13,14 @@ async function searchUsers (req, res) { res.send(result.result) } +/** + * Search for skills in organization + */ +async function searchSkills (req, res) { + const result = await esHelper.searchSkillsInOrganization(req.query) + res.send(result.result) +} + /** * Search for attribute values */ @@ -31,6 +39,7 @@ async function searchAchievementValues (req, res) { module.exports = { searchUsers, + searchSkills, searchAttributeValues, searchAchievementValues } diff --git a/src/modules/search/route.js b/src/modules/search/route.js index 6fa3899..f2219ee 100644 --- a/src/modules/search/route.js +++ b/src/modules/search/route.js @@ -14,6 +14,14 @@ module.exports = { scopes: ['read:user', 'all:user'] } }, + '/search/skills': { + get: { + method: Controller.searchSkills + auth: 'jwt', + access: consts.AdminUser, + scopes: ['create:userAttribute', 'all:userAttribute'] + } + }, '/search/userAttributes': { get: { method: Controller.searchAttributeValues, From f15e7969b83e6cb95b1c4b481397342f5769ca6c Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Mon, 3 Aug 2020 17:01:18 +0530 Subject: [PATCH 85/93] Fix syntax error --- src/modules/search/route.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/search/route.js b/src/modules/search/route.js index f2219ee..77c47bd 100644 --- a/src/modules/search/route.js +++ b/src/modules/search/route.js @@ -16,7 +16,7 @@ module.exports = { }, '/search/skills': { get: { - method: Controller.searchSkills + method: Controller.searchSkills, auth: 'jwt', access: consts.AdminUser, scopes: ['create:userAttribute', 'all:userAttribute'] From 9a91748002ff295dc1643fcccfb982a0eefcc308 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Mon, 3 Aug 2020 17:39:51 +0600 Subject: [PATCH 86/93] Include search/skills endpoint --- docs/UBahn_API.postman_collection.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/UBahn_API.postman_collection.json b/docs/UBahn_API.postman_collection.json index 683d90b..e86ffe3 100644 --- a/docs/UBahn_API.postman_collection.json +++ b/docs/UBahn_API.postman_collection.json @@ -143,13 +143,13 @@ } ], "url": { - "raw": "{{HOST}}/search/userAchievements?organizationId=36ed815b-3da1-49f1-a043-aaed0a4e81ad&keyword=Topcoder", + "raw": "{{HOST}}/search/skills?organizationId=36ed815b-3da1-49f1-a043-aaed0a4e81ad&keyword=net", "host": [ "{{HOST}}" ], "path": [ "search", - "userAchievements" + "skills" ], "query": [ { @@ -158,7 +158,7 @@ }, { "key": "keyword", - "value": "Topcoder" + "value": "net" } ] } From b6125dcb1ffb5685e00a98ce8193ceb920ad8109 Mon Sep 17 00:00:00 2001 From: Rakib Ansary Saikot Date: Mon, 3 Aug 2020 18:09:29 +0600 Subject: [PATCH 87/93] Extract skillProviderId instead of skill.id --- src/common/es-helper.js | 3 +-- src/modules/search/route.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index a4474f4..046e817 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1436,7 +1436,6 @@ async function searchUsers (authUser, filter, params) { * 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 }) { const esQueryToGetSkillProviders = buildEsQueryToGetSkillProviderIds(organizationId) logger.debug(`ES query to get skill provider ids: ${JSON.stringify(esQueryToGetSkillProviders, null, 2)}`) @@ -1444,7 +1443,7 @@ async function searchSkillsInOrganization ({ organizationId, keyword }) { 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.id))) + 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) diff --git a/src/modules/search/route.js b/src/modules/search/route.js index f2219ee..77c47bd 100644 --- a/src/modules/search/route.js +++ b/src/modules/search/route.js @@ -16,7 +16,7 @@ module.exports = { }, '/search/skills': { get: { - method: Controller.searchSkills + method: Controller.searchSkills, auth: 'jwt', access: consts.AdminUser, scopes: ['create:userAttribute', 'all:userAttribute'] From 97f0ab43906e61fd5943ac068494c67496a5126d Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 4 Aug 2020 15:25:13 +0530 Subject: [PATCH 88/93] #29 - Proxy to EMSI if the org use EMSI as skill provider --- config/default.js | 11 +++++ package-lock.json | 13 +++++ package.json | 1 + src/modules/search/controller.js | 3 +- src/modules/search/service.js | 84 ++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/modules/search/service.js diff --git a/config/default.js b/config/default.js index f88ffea..af863d0 100755 --- a/config/default.js +++ b/config/default.js @@ -36,6 +36,17 @@ module.exports = { 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', diff --git a/package-lock.json b/package-lock.json index fae6e60..5a98387 100644 --- a/package-lock.json +++ b/package-lock.json @@ -481,6 +481,11 @@ "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", @@ -2281,6 +2286,14 @@ "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", diff --git a/package.json b/package.json index ac33b31..3defd2e 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "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", diff --git a/src/modules/search/controller.js b/src/modules/search/controller.js index b29bfcf..5412aec 100644 --- a/src/modules/search/controller.js +++ b/src/modules/search/controller.js @@ -3,6 +3,7 @@ */ const esHelper = require('../../common/es-helper') const { injectSearchMeta } = require('../../common/helper') +const service = require('./service') /** * Search for users. Returns enriched users @@ -17,7 +18,7 @@ async function searchUsers (req, res) { * Search for skills in organization */ async function searchSkills (req, res) { - const result = await esHelper.searchSkillsInOrganization(req.query) + const result = await service.getSkills(req.query, req.auth) res.send(result.result) } 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 } From bedfd3f505c005dad4f50f8dccf6a55641e2e946 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 4 Aug 2020 19:38:58 +0530 Subject: [PATCH 89/93] ExternalId for skill cannot be a required field because in some scenarios, the skill is not coming from an external source but directly fed into ubahn --- src/modules/skill/service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js index f5a80a9..b774e64 100644 --- a/src/modules/skill/service.js +++ b/src/modules/skill/service.js @@ -11,7 +11,7 @@ const methods = helper.getServiceMethods( skillProviderId: joi.string().required(), name: joi.string().required(), uri: joi.string(), - externalId: joi.string().required() + externalId: joi.string() }, { skillProviderId: joi.string(), From 9ab468102a8ad0b98a3177b180a3e6b3c1763ef7 Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Tue, 4 Aug 2020 20:16:09 +0530 Subject: [PATCH 90/93] Fix issue where we could not create skills with different name --- src/modules/skill/service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/skill/service.js b/src/modules/skill/service.js index b774e64..a869e88 100644 --- a/src/modules/skill/service.js +++ b/src/modules/skill/service.js @@ -37,7 +37,7 @@ const methods = helper.getServiceMethods( } return dbQueries }, - [['skillProviderId', 'externalId']] + [['skillProviderId', 'externalId', 'name']] ) module.exports = { From fadd8d0fdc6e706dd4d8961b99173716301ad5cd Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 6 Aug 2020 16:11:43 +0530 Subject: [PATCH 91/93] Misc --- src/common/es-helper.js | 10 ++++++++++ src/modules/usersRole/route.js | 6 ------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index 046e817..dda4f60 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1340,6 +1340,10 @@ 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 } @@ -1437,6 +1441,9 @@ async function searchUsers (authUser, filter, params) { * @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)}`) @@ -1488,6 +1495,9 @@ async function searchAttributeValues ({ attributeId, attributeValue }) { } 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)}`) diff --git a/src/modules/usersRole/route.js b/src/modules/usersRole/route.js index 3b369ec..7ec1fee 100644 --- a/src/modules/usersRole/route.js +++ b/src/modules/usersRole/route.js @@ -38,12 +38,6 @@ module.exports = { access: consts.AllAuthenticatedUsers, scopes: ['read:usersRole', 'all:usersRole'] }, - patch: { - method: Controller.patch, - auth: 'jwt', - access: consts.AllAuthenticatedUsers, - scopes: ['update:usersRole', 'all:usersRole'] - }, delete: { method: Controller.remove, auth: 'jwt', From db7a85669a183e28e152e727a7289e19558464dc Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 6 Aug 2020 19:42:26 +0530 Subject: [PATCH 92/93] #31 - Support non admin roles --- src/common/es-helper.js | 12 ++++++++++-- src/common/service-helper.js | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/common/es-helper.js b/src/common/es-helper.js index f9ea4bb..c251875 100644 --- a/src/common/es-helper.js +++ b/src/common/es-helper.js @@ -1042,7 +1042,11 @@ async function searchElasticSearch (resource, ...args) { } // for non-admin, only return entities that the user created - if (authUser.roles && !checkIfExists(authUser.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { + if ( + authUser.roles && + !checkIfExists(authUser.roles, appConst.AdminUser) && + !checkIfExists(authUser.roles, [appConst.UserRoles.ubahn]) + ) { setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') } @@ -1197,7 +1201,11 @@ async function searchUsers (authUser, filter, params) { } // for non-admin, only return entities that the user created - if (authUser.roles && !checkIfExists(authUser.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { + if ( + authUser.roles && + !checkIfExists(authUser.roles, appConst.AdminUser) && + !checkIfExists(authUser.roles, [appConst.UserRoles.ubahn]) + ) { setFilterValueToEsQuery(esQuery, 'createdBy', getAuthUser(authUser), 'createdBy') } diff --git a/src/common/service-helper.js b/src/common/service-helper.js index 57f58b3..98c2c58 100644 --- a/src/common/service-helper.js +++ b/src/common/service-helper.js @@ -267,7 +267,11 @@ function getServiceMethods (Model, createSchema, patchSchema, searchSchema, buil // user token // for non-admin users, this endpoint will only return entities that the user has created. - if (auth.roles && !checkIfExists(auth.roles, [appConst.UserRoles.admin, appConst.UserRoles.administrator])) { + 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) From 2a6d709364b8e1023368d976bb1bb585aac3555e Mon Sep 17 00:00:00 2001 From: Mithun Kamath Date: Thu, 6 Aug 2020 20:13:15 +0530 Subject: [PATCH 93/93] #31 - Support non admin roles for search endpoints --- src/modules/search/route.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/search/route.js b/src/modules/search/route.js index 77c47bd..4eb0c08 100644 --- a/src/modules/search/route.js +++ b/src/modules/search/route.js @@ -10,7 +10,7 @@ module.exports = { post: { method: Controller.searchUsers, auth: 'jwt', - access: consts.AdminUser, + access: [...consts.AdminUser, consts.UserRoles.ubahn], scopes: ['read:user', 'all:user'] } }, @@ -18,7 +18,7 @@ module.exports = { get: { method: Controller.searchSkills, auth: 'jwt', - access: consts.AdminUser, + access: [...consts.AdminUser, consts.UserRoles.ubahn], scopes: ['create:userAttribute', 'all:userAttribute'] } }, @@ -26,7 +26,7 @@ module.exports = { get: { method: Controller.searchAttributeValues, auth: 'jwt', - access: consts.AdminUser, + access: [...consts.AdminUser, consts.UserRoles.ubahn], scopes: ['create:userAttribute', 'all:userAttribute'] } }, @@ -34,7 +34,7 @@ module.exports = { get: { method: Controller.searchAchievementValues, auth: 'jwt', - access: consts.AdminUser, + access: [...consts.AdminUser, consts.UserRoles.ubahn], scopes: ['create:userAttribute', 'all:userAttribute'] } }