diff --git a/app/scripts/app.js b/app/scripts/app.js index d11cf22..ac7c505 100644 --- a/app/scripts/app.js +++ b/app/scripts/app.js @@ -41,6 +41,7 @@ var yaml = require('js-yaml'); var $RefParser = require("@apidevtools/json-schema-ref-parser"); var airr = require('airr-js'); var vdj_schema = require('vdjserver-schema'); +const swaggerUi = require('swagger-ui-express'); // Express app @@ -96,21 +97,6 @@ var tenantController = require('./controllers/tenantController'); // load API spec var api_spec = yaml.safeLoad(fs.readFileSync(path.resolve(__dirname, '../../swagger/vdjserver-api.yaml'), 'utf8')); -// load AIRR Standards spec, openapi v3 -//var airr_spec = yaml.safeLoad(fs.readFileSync(path.resolve(__dirname, '../airr-standards/specs/airr-schema-openapi3.yaml'), 'utf8')); -// fix up swagger v2 spec for openapi v3 -/* for (var obj in airr_spec) { - // discriminator is an object vs string - if (airr_spec[obj]['discriminator']) - airr_spec[obj]['discriminator'] = { propertyName: airr_spec[obj]['discriminator'] }; - // add nullable flags - for (var prop in airr_spec[obj]['properties']) { - var p = airr_spec[obj]['properties'][prop]; - if ((p['x-airr']) && (p['x-airr']['nullable'])) { - p['nullable'] = true; - } - } -} */ config.log.info(context, 'Using query collection suffix: ' + mongoSettings.queryCollection); config.log.info(context, 'Using load collection suffix: ' + mongoSettings.loadCollection); @@ -198,6 +184,10 @@ ServiceAccount.getToken() .then(function(api_schema) { //console.log(JSON.stringify(api_schema,null,2)); + // enable swagger ui + config.log.info(context, 'API swagger ui available at /api/v2/api-ui'); + app.use('/api/v2/api-ui', swaggerUi.serve, swaggerUi.setup(api_schema)); + // wrap the operations functions to catch syntax errors and such // we do not get a good stack trace with the middleware error handler var try_function = async function (request, response, the_function) { @@ -235,6 +225,8 @@ ServiceAccount.getToken() operations: { //getStatus: function(req, res) { res.send('{"result":"success"}'); } getStatus: async function(req, res) { return try_function(req, res, apiResponseController.confirmUpStatus); }, + //getDocs: async function(req, res) { return Promise.resolve(); }, + //getDocsUI: async function(req, res) { return Promise.resolve(); }, getTenants: async function(req, res) { return try_function(req, res, tenantController.getTenants); }, // authentication @@ -246,6 +238,7 @@ ServiceAccount.getToken() // user createUser: async function(req, res) { return try_function(req, res, userController.createUser); }, getUserProfile: async function(req, res) { return try_function(req, res, userController.getUserProfile); }, + getUserIdentity: async function(req, res) { return try_function(req, res, userController.getUserIdentity); }, duplicateUsername: async function(req, res) { return try_function(req, res, userController.duplicateUsername); }, verifyUser: async function(req, res) { return try_function(req, res, userController.verifyUser); }, resendVerifyEmail: async function(req, res) { return try_function(req, res, userController.resendVerificationEmail); }, diff --git a/app/scripts/controllers/userController.js b/app/scripts/controllers/userController.js index 21e2885..56f532c 100644 --- a/app/scripts/controllers/userController.js +++ b/app/scripts/controllers/userController.js @@ -239,6 +239,34 @@ UserController.getUserProfile = async function(request, response) { return apiResponseController.sendSuccess(profile, response); }; +// get all user profiles +// we only return the usernames for this endpoint +UserController.getUserIdentity = async function(request, response) { + const context = 'UserController.getUserIdentity'; + var msg = null; + + config.log.info(context, 'querying all user profiles.'); + + // get user profile + var profiles = await tapisIO.getAllUserProfiles() + .catch(function(error) { + msg = 'got error retrieving user profiles, error: ' + error; + }); + if (msg) { + msg = config.log.error(context, msg); + webhookIO.postToSlack(msg); + return apiResponseController.sendError(msg, 500, response); + } + //console.log(JSON.stringify(profiles, null, 2)); + + var names = []; + for (let i in profiles) + if (profiles[i]['value']['username']) + names.push({ username: profiles[i]['value']['username'] }); + + return apiResponseController.sendSuccess(names, response); +}; + // check if username is already being used UserController.duplicateUsername = async function(request, response) { const context = 'UserController.duplicateUsername'; diff --git a/app/vdj-tapis-js b/app/vdj-tapis-js index 4f95931..fa42d6f 160000 --- a/app/vdj-tapis-js +++ b/app/vdj-tapis-js @@ -1 +1 @@ -Subproject commit 4f9593185bedece514a13d5bc2cce69da3d52c91 +Subproject commit fa42d6fef4cac27b4199e7a6139acc279ff7b90a diff --git a/package.json b/package.json index 8dc2cb1..a735e11 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "recaptcha-v2": "0.1.3", "request": ">=2.68.0", "socket.io": ">=2.4.0", + "swagger-ui-express": "^4.1.6", "tar": "^6.1.2", "uuid": "^9.0.1", "vdj-tapis-js": "file:app/vdj-tapis-js", diff --git a/swagger/vdjserver-api.yaml b/swagger/vdjserver-api.yaml index 2744f2b..c6b0d11 100644 --- a/swagger/vdjserver-api.yaml +++ b/swagger/vdjserver-api.yaml @@ -23,7 +23,7 @@ # along with this program. If not, see . # -openapi: '3.0' +openapi: '3.0.0' info: title: VDJServer API version: '2.0.0' @@ -45,6 +45,11 @@ servers: variables: basePath: default: /api/v2 + - url: http://localhost:8080/api/v2 + description: Local development server + variables: + basePath: + default: /api/v2 # Even though we point to the VDJServer Schema, most services replace this with the # local copy of the schema. @@ -92,6 +97,37 @@ paths: schema: type: string +# These are handled internally so not expected to be defined, +# but left here as documentation +# +# /api-docs: +# get: +# operationId: getDocs +# description: | +# API documentation. +# responses: +# '200': +# description: | +# Success. +# content: +# application/json: +# schema: +# type: object +# +# /api-ui: +# get: +# operationId: getDocsUI +# description: | +# API swagger ui. +# responses: +# '200': +# description: | +# Success. +# content: +# test/html: +# schema: +# type: string + /tenants: get: description: | @@ -1390,6 +1426,28 @@ paths: '500': description: Internal service error occurred. + /user/identity: + get: + description: get all user profiles + operationId: getUserIdentity + tags: + - user + security: + - user_authorization: [] + responses: + '200': + description: Success. + content: + application/json: + schema: + $ref: '#/components/schemas/BasicResponse' + '400': + description: Bad request. + '401': + description: Not authorized. + '500': + description: Internal service error occurred. + /user/duplicate/{username}: get: description: check for duplicate username