From 7a8123eb52d4701c4540f08b4d43b2a0b45589d2 Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Thu, 11 Nov 2021 03:20:19 +0500 Subject: [PATCH] chore: docs finalized for stp and others --- api/public/swagger.yaml | 121 +++++- api/src/controllers/auth.ts | 8 +- api/src/controllers/client.ts | 2 +- api/src/controllers/drive.ts | 13 +- api/src/controllers/group.ts | 12 +- api/src/controllers/stp.ts | 151 +++++++ api/src/controllers/user.ts | 10 +- api/src/routes/api/drive.ts | 9 +- api/src/routes/api/stp.ts | 114 ++--- api/src/routes/web/web.ts | 4 +- api/src/utils/validation.ts | 7 + api/tsoa.json | 4 + public/swagger.yaml | 782 ---------------------------------- web/.env.example | 2 +- 14 files changed, 319 insertions(+), 920 deletions(-) create mode 100644 api/src/controllers/stp.ts delete mode 100644 public/swagger.yaml diff --git a/api/public/swagger.yaml b/api/public/swagger.yaml index 502bc02..d48f039 100644 --- a/api/public/swagger.yaml +++ b/api/public/swagger.yaml @@ -151,6 +151,28 @@ components: - tree type: object additionalProperties: false + ExecuteReturnJsonResponse: + properties: + status: + type: string + log: + type: string + result: + type: string + message: + type: string + required: + - status + type: object + additionalProperties: false + ExecuteReturnJsonPayload: + properties: + _program: + type: string + description: 'Location of SAS program' + example: /Public/somefolder/some.file + type: object + additionalProperties: false UserResponse: properties: id: @@ -402,7 +424,7 @@ paths: examples: 'Example 1': value: {status: failure, message: 'Deployment failed!'} - description: 'Creates/updates files within SASjs Drive using provided payload.' + summary: 'Creates/updates files within SASjs Drive using provided payload.' tags: - Drive security: @@ -437,7 +459,7 @@ paths: examples: 'Example 1': value: {status: failure, message: 'File request failed.'} - description: 'Get file from SASjs Drive' + summary: 'Get file from SASjs Drive' tags: - Drive security: @@ -450,6 +472,7 @@ paths: required: true schema: type: string + example: /Public/somefolder/some.file patch: operationId: UpdateFile responses: @@ -471,7 +494,7 @@ paths: examples: 'Example 1': value: {status: failure, message: 'File request failed.'} - description: 'Modify a file in SASjs Drive' + summary: 'Modify a file in SASjs Drive' tags: - Drive security: @@ -494,13 +517,68 @@ paths: application/json: schema: $ref: '#/components/schemas/GetFileTreeResponse' - description: 'Fetch file tree within SASjs Drive.' + summary: 'Fetch file tree within SASjs Drive.' tags: - Drive security: - bearerAuth: [] parameters: [] + /SASjsApi/client/execute: + get: + operationId: ExecuteReturnRaw + responses: + '200': + description: Ok + content: + application/json: + schema: + type: string + description: "Trigger a SAS program using it's location in the _program parameter.\nEnable debugging using the _debug parameter.\nAdditional URL parameters are turned into SAS macro variables.\nAny files provided are placed into the session and\ncorresponding _WEBIN_XXX variables are created." + summary: 'Execute Stored Program, return raw content' + tags: + - STP + security: + - + bearerAuth: [] + parameters: + - + in: query + name: _program + required: true + schema: + type: string + example: /Public/somefolder/some.file + post: + operationId: ExecuteReturnJson + responses: + '200': + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/ExecuteReturnJsonResponse' + description: "Trigger a SAS program using it's location in the _program parameter.\nEnable debugging using the _debug parameter.\nAdditional URL parameters are turned into SAS macro variables.\nAny files provided are placed into the session and\ncorresponding _WEBIN_XXX variables are created." + summary: 'Execute Stored Program, return JSON' + tags: + - STP + security: + - + bearerAuth: [] + parameters: + - + in: query + name: _program + required: false + schema: + type: string + example: /Public/somefolder/some.file + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ExecuteReturnJsonPayload' /SASjsApi/user: get: operationId: GetAllUsers @@ -516,7 +594,7 @@ paths: examples: 'Example 1': value: [{id: 123, username: johnusername, displayName: John}, {id: 456, username: starkusername, displayName: Stark}] - description: 'Get list of all users (username, displayname). All users can request this.' + summary: 'Get list of all users (username, displayname). All users can request this.' tags: - User security: @@ -535,7 +613,7 @@ paths: examples: 'Example 1': value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true} - description: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.' + summary: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.' tags: - User security: @@ -558,7 +636,7 @@ paths: application/json: schema: $ref: '#/components/schemas/UserDetailsResponse' - description: 'Get user properties - such as group memberships, userName, displayName.' + summary: 'Get user properties - such as group memberships, userName, displayName.' tags: - User security: @@ -586,7 +664,7 @@ paths: examples: 'Example 1': value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true} - description: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.' + summary: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.' tags: - User security: @@ -613,7 +691,7 @@ paths: responses: '204': description: 'No content' - description: 'Delete a user. Can be performed either by admins, or the user in question.' + summary: 'Delete a user. Can be performed either by admins, or the user in question.' tags: - User security: @@ -653,7 +731,7 @@ paths: examples: 'Example 1': value: [{groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users'}] - description: 'Get list of all groups (groupName and groupDescription). All users can request this.' + summary: 'Get list of all groups (groupName and groupDescription). All users can request this.' tags: - Group security: @@ -672,7 +750,7 @@ paths: examples: 'Example 1': value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Create a new group. Admin only.' + summary: 'Create a new group. Admin only.' tags: - Group security: @@ -695,7 +773,7 @@ paths: application/json: schema: $ref: '#/components/schemas/GroupDetailsResponse' - description: 'Get list of members of a group (userName). All users can request this.' + summary: 'Get list of members of a group (userName). All users can request this.' tags: - Group security: @@ -716,7 +794,7 @@ paths: responses: '204': description: 'No content' - description: 'Delete a group. Admin task only.' + summary: 'Delete a group. Admin task only.' tags: - Group security: @@ -745,7 +823,7 @@ paths: examples: 'Example 1': value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Add a user to a group. Admin task only.' + summary: 'Add a user to a group. Admin task only.' tags: - Group security: @@ -782,7 +860,7 @@ paths: examples: 'Example 1': value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Remove a user to a group. Admin task only.' + summary: 'Remove a user to a group. Admin task only.' tags: - Group security: @@ -820,7 +898,7 @@ paths: examples: 'Example 1': value: {clientId: someFormattedClientID1234, clientSecret: someRandomCryptoString} - description: 'Create client with the following attributes: ClientId, ClientSecret. Admin only task.' + summary: 'Create client with the following attributes: ClientId, ClientSecret. Admin only task.' tags: - Client security: @@ -846,7 +924,7 @@ paths: examples: 'Example 1': value: {code: someRandomCryptoString} - description: 'Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE' + summary: 'Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE' tags: - Auth security: [] @@ -870,7 +948,7 @@ paths: examples: 'Example 1': value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString} - description: 'Accepts client/auth code and returns access/refresh tokens' + summary: 'Accepts client/auth code and returns access/refresh tokens' tags: - Auth security: [] @@ -894,7 +972,7 @@ paths: examples: 'Example 1': value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString} - description: 'Returns new access/refresh tokens' + summary: 'Returns new access/refresh tokens' tags: - Auth security: @@ -907,7 +985,7 @@ paths: responses: '204': description: 'No content' - description: 'Logout terminate access/refresh tokens and returns nothing' + summary: 'Logout terminate access/refresh tokens and returns nothing' tags: - Auth security: @@ -933,3 +1011,6 @@ tags: - name: Group description: 'Operations about group' + - + name: STP + description: 'Operations about STP' diff --git a/api/src/controllers/auth.ts b/api/src/controllers/auth.ts index f49eed3..559c5f1 100644 --- a/api/src/controllers/auth.ts +++ b/api/src/controllers/auth.ts @@ -19,7 +19,7 @@ export default class AuthController { delete AuthController.authCodes[userId][clientId] /** - * Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE + * @summary Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE * */ @Example({ @@ -33,7 +33,7 @@ export default class AuthController { } /** - * Accepts client/auth code and returns access/refresh tokens + * @summary Accepts client/auth code and returns access/refresh tokens * */ @Example({ @@ -46,7 +46,7 @@ export default class AuthController { } /** - * Returns new access/refresh tokens + * @summary Returns new access/refresh tokens * */ @Example({ @@ -62,7 +62,7 @@ export default class AuthController { } /** - * Logout terminate access/refresh tokens and returns nothing + * @summary Logout terminate access/refresh tokens and returns nothing * */ @Security('bearerAuth') diff --git a/api/src/controllers/client.ts b/api/src/controllers/client.ts index 86f2ec7..cfc5c0a 100644 --- a/api/src/controllers/client.ts +++ b/api/src/controllers/client.ts @@ -7,7 +7,7 @@ import Client, { ClientPayload } from '../model/Client' @Tags('Client') export default class ClientController { /** - * Create client with the following attributes: ClientId, ClientSecret. Admin only task. + * @summary Create client with the following attributes: ClientId, ClientSecret. Admin only task. * */ @Example({ diff --git a/api/src/controllers/drive.ts b/api/src/controllers/drive.ts index f3971d2..733a110 100644 --- a/api/src/controllers/drive.ts +++ b/api/src/controllers/drive.ts @@ -13,7 +13,7 @@ import { import { fileExists, readFile, createFile } from '@sasjs/utils' import { createFileTree, ExecutionController, getTreeExample } from '.' -import { FileTree, isFileQuery, isFileTree, TreeNode } from '../types' +import { FileTree, isFileTree, TreeNode } from '../types' import path from 'path' import { getTmpFilesFolderPath } from '../utils' @@ -77,7 +77,7 @@ const execDeployErrorResponse: DeployResponse = { @Tags('Drive') export class DriveController { /** - * Creates/updates files within SASjs Drive using provided payload. + * @summary Creates/updates files within SASjs Drive using provided payload. * */ @Example(successDeployResponse) @@ -89,8 +89,9 @@ export class DriveController { } /** - * Get file from SASjs Drive - * + * @summary Get file from SASjs Drive + * @query filePath Location of SAS program + * @example filePath "/Public/somefolder/some.file" */ @Example({ status: 'success', @@ -106,7 +107,7 @@ export class DriveController { } /** - * Modify a file in SASjs Drive + * @summary Modify a file in SASjs Drive * */ @Example({ @@ -124,7 +125,7 @@ export class DriveController { } /** - * Fetch file tree within SASjs Drive. + * @summary Fetch file tree within SASjs Drive. * */ @Get('/filetree') diff --git a/api/src/controllers/group.ts b/api/src/controllers/group.ts index 5371201..1e98dd0 100644 --- a/api/src/controllers/group.ts +++ b/api/src/controllers/group.ts @@ -33,7 +33,7 @@ interface GroupDetailsResponse { @Tags('Group') export default class GroupController { /** - * Get list of all groups (groupName and groupDescription). All users can request this. + * @summary Get list of all groups (groupName and groupDescription). All users can request this. * */ @Example([ @@ -49,7 +49,7 @@ export default class GroupController { } /** - * Create a new group. Admin only. + * @summary Create a new group. Admin only. * */ @Example({ @@ -67,7 +67,7 @@ export default class GroupController { } /** - * Get list of members of a group (userName). All users can request this. + * @summary Get list of members of a group (userName). All users can request this. * @param groupId The group's identifier * @example groupId 1234 */ @@ -79,7 +79,7 @@ export default class GroupController { } /** - * Add a user to a group. Admin task only. + * @summary Add a user to a group. Admin task only. * @param groupId The group's identifier * @example groupId "1234" * @param userId The user's identifier @@ -101,7 +101,7 @@ export default class GroupController { } /** - * Remove a user to a group. Admin task only. + * @summary Remove a user to a group. Admin task only. * @param groupId The group's identifier * @example groupId "1234" * @param userId The user's identifier @@ -123,7 +123,7 @@ export default class GroupController { } /** - * Delete a group. Admin task only. + * @summary Delete a group. Admin task only. * @param groupId The group's identifier * @example groupId 1234 */ diff --git a/api/src/controllers/stp.ts b/api/src/controllers/stp.ts new file mode 100644 index 0000000..20e495e --- /dev/null +++ b/api/src/controllers/stp.ts @@ -0,0 +1,151 @@ +import express from 'express' +import path from 'path' +import { + Request, + Security, + Route, + Tags, + Example, + Post, + Body, + Get, + Query +} from 'tsoa' +import { ExecutionController } from '.' +import { PreProgramVars } from '../types' +import { getTmpFilesFolderPath, makeFilesNamesMap } from '../utils' + +interface ExecuteReturnJsonPayload { + /** + * Location of SAS program + * @example "/Public/somefolder/some.file" + */ + _program?: string +} +interface ExecuteReturnJsonResponse { + status: string + log?: string + result?: string + message?: string +} + +@Security('bearerAuth') +@Route('SASjsApi/client') +@Tags('STP') +export default class STPController { + /** + * Trigger a SAS program using it's location in the _program parameter. + * Enable debugging using the _debug parameter. + * Additional URL parameters are turned into SAS macro variables. + * Any files provided are placed into the session and + * corresponding _WEBIN_XXX variables are created. + * @summary Execute Stored Program, return raw content + * @query _program Location of SAS program + * @example _program "/Public/somefolder/some.file" + */ + @Get('/execute') + public async executeReturnRaw( + @Request() request: express.Request, + @Query() _program: string + ): Promise { + return executeReturnRaw(request, _program) + } + /** + * Trigger a SAS program using it's location in the _program parameter. + * Enable debugging using the _debug parameter. + * Additional URL parameters are turned into SAS macro variables. + * Any files provided are placed into the session and + * corresponding _WEBIN_XXX variables are created. + * @summary Execute Stored Program, return JSON + * @query _program Location of SAS program + * @example _program "/Public/somefolder/some.file" + */ + @Post('/execute') + public async executeReturnJson( + @Request() request: express.Request, + @Body() body: ExecuteReturnJsonPayload, + @Query() _program?: string + ): Promise { + const program = _program ?? body._program + return executeReturnJson(request, program!) + } +} + +const executeReturnRaw = async ( + req: express.Request, + _program: string +): Promise => { + const sasCodePath = + path + .join(getTmpFilesFolderPath(), _program) + .replace(new RegExp('/', 'g'), path.sep) + '.sas' + + try { + const result = await new ExecutionController().execute( + sasCodePath, + getPreProgramVariables(req), + undefined, + undefined, + { + ...req.query + } + ) + + return result as string + } catch (err) { + throw { + code: 400, + status: 'failure', + message: 'Job execution failed.', + ...(typeof err === 'object' ? err : { details: err }) + } + } +} + +const executeReturnJson = async ( + req: any, + _program: string +): Promise => { + const sasCodePath = + path + .join(getTmpFilesFolderPath(), _program) + .replace(new RegExp('/', 'g'), path.sep) + '.sas' + + const filesNamesMap = req.files?.length ? makeFilesNamesMap(req.files) : null + + try { + const jsonResult: any = await new ExecutionController().execute( + sasCodePath, + getPreProgramVariables(req), + undefined, + req.sasSession, + { ...req.query, ...req.body }, + { filesNamesMap: filesNamesMap }, + true + ) + return { + status: 'success', + result: jsonResult.result, + log: jsonResult.log + } + } catch (err) { + throw { + status: 'failure', + message: 'Job execution failed.', + ...(typeof err === 'object' ? err : { details: err }) + } + } +} + +const getPreProgramVariables = (req: any): PreProgramVars => { + const host = req.get('host') + const protocol = req.protocol + '://' + const { user, accessToken } = req + return { + username: user.username, + userId: user.userId, + displayName: user.displayName, + serverUrl: protocol + host, + accessToken + } +} diff --git a/api/src/controllers/user.ts b/api/src/controllers/user.ts index 8baa565..4f56a1d 100644 --- a/api/src/controllers/user.ts +++ b/api/src/controllers/user.ts @@ -34,7 +34,7 @@ interface UserDetailsResponse { @Tags('User') export default class UserController { /** - * Get list of all users (username, displayname). All users can request this. + * @summary Get list of all users (username, displayname). All users can request this. * */ @Example([ @@ -55,7 +55,7 @@ export default class UserController { } /** - * Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task. + * @summary Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task. * */ @Example({ @@ -73,7 +73,7 @@ export default class UserController { } /** - * Get user properties - such as group memberships, userName, displayName. + * @summary Get user properties - such as group memberships, userName, displayName. * @param userId The user's identifier * @example userId 1234 */ @@ -83,7 +83,7 @@ export default class UserController { } /** - * Update user properties - such as displayName. Can be performed either by admins, or the user in question. + * @summary Update user properties - such as displayName. Can be performed either by admins, or the user in question. * @param userId The user's identifier * @example userId "1234" */ @@ -103,7 +103,7 @@ export default class UserController { } /** - * Delete a user. Can be performed either by admins, or the user in question. + * @summary Delete a user. Can be performed either by admins, or the user in question. * @param userId The user's identifier * @example userId 1234 */ diff --git a/api/src/routes/api/drive.ts b/api/src/routes/api/drive.ts index ef739f0..eddf773 100644 --- a/api/src/routes/api/drive.ts +++ b/api/src/routes/api/drive.ts @@ -1,13 +1,6 @@ import express from 'express' -import path from 'path' -import { ExecutionController } from '../../controllers' import { DriveController } from '../../controllers/drive' -import { isFileQuery } from '../../types' -import { - getFileDriveValidation, - getTmpFilesFolderPath, - updateFileDriveValidation -} from '../../utils' +import { getFileDriveValidation, updateFileDriveValidation } from '../../utils' const driveRouter = express.Router() diff --git a/api/src/routes/api/stp.ts b/api/src/routes/api/stp.ts index e891a97..33ea8da 100644 --- a/api/src/routes/api/stp.ts +++ b/api/src/routes/api/stp.ts @@ -1,39 +1,26 @@ import express from 'express' -import { isExecutionQuery, PreProgramVars } from '../../types' -import path from 'path' -import { getTmpFilesFolderPath, makeFilesNamesMap } from '../../utils' -import { ExecutionController, FileUploadController } from '../../controllers' +import { executeProgramRawValidation } from '../../utils' +import STPController from '../../controllers/stp' +import { FileUploadController } from '../../controllers' const stpRouter = express.Router() const fileUploadController = new FileUploadController() +const controller = new STPController() stpRouter.get('/execute', async (req, res) => { - if (isExecutionQuery(req.query)) { - let sasCodePath = - path - .join(getTmpFilesFolderPath(), req.query._program) - .replace(new RegExp('/', 'g'), path.sep) + '.sas' + const { error, value: query } = executeProgramRawValidation(req.query) + if (error) return res.status(400).send(error.details[0].message) - await new ExecutionController() - .execute(sasCodePath, getPreProgramVariables(req), undefined, undefined, { - ...req.query - }) - .then((result: {}) => { - res.status(200).send(result) - }) - .catch((err: {} | string) => { - res.status(400).send({ - status: 'failure', - message: 'Job execution failed.', - ...(typeof err === 'object' ? err : { details: err }) - }) - }) - } else { - res.status(400).send({ - status: 'failure', - message: `Please provide the location of SAS code` - }) + try { + const response = await controller.executeReturnRaw(req, query._program) + res.send(response) + } catch (err: any) { + const statusCode = err.code + + delete err.code + + res.status(statusCode).send(err) } }) @@ -42,68 +29,27 @@ stpRouter.post( fileUploadController.preuploadMiddleware, fileUploadController.getMulterUploadObject().any(), async (req: any, res: any) => { - let _program - if (isExecutionQuery(req.query)) { - _program = req.query._program - } else if (isExecutionQuery(req.body)) { - _program = req.body._program - } + const { error: errQ, value: query } = executeProgramRawValidation(req.query) + if (errQ) return res.status(400).send(errQ.details[0].message) - if (_program) { - let sasCodePath = - path - .join(getTmpFilesFolderPath(), _program) - .replace(new RegExp('/', 'g'), path.sep) + '.sas' + const { error: errB, value: body } = executeProgramRawValidation(req.body) + if (errB) return res.status(400).send(errB.details[0].message) - let filesNamesMap = null + try { + const response = await controller.executeReturnJson( + req, + query, + body?._program + ) + res.send(response) + } catch (err: any) { + const statusCode = err.code - if (req.files && req.files.length > 0) { - filesNamesMap = makeFilesNamesMap(req.files) - } + delete err.code - await new ExecutionController() - .execute( - sasCodePath, - getPreProgramVariables(req), - undefined, - req.sasSession, - { ...req.query, ...req.body }, - { filesNamesMap: filesNamesMap }, - true - ) - .then((result: {}) => { - res.status(200).send({ - status: 'success', - ...result - }) - }) - .catch((err: {} | string) => { - res.status(400).send({ - status: 'failure', - message: 'Job execution failed.', - ...(typeof err === 'object' ? err : { details: err }) - }) - }) - } else { - res.status(400).send({ - status: 'failure', - message: `Please provide the location of SAS code` - }) + res.status(statusCode).send(err) } } ) -const getPreProgramVariables = (req: any): PreProgramVars => { - const host = req.get('host') - const protocol = req.protocol + '://' - const { user, accessToken } = req - return { - username: user.username, - userId: user.userId, - displayName: user.displayName, - serverUrl: protocol + host, - accessToken - } -} - export default stpRouter diff --git a/api/src/routes/web/web.ts b/api/src/routes/web/web.ts index 87a87b2..d6e61c6 100644 --- a/api/src/routes/web/web.ts +++ b/api/src/routes/web/web.ts @@ -1,8 +1,6 @@ import express from 'express' -import { isExecutionQuery } from '../../types' import path from 'path' -import { getTmpFilesFolderPath, getWebBuildFolderPath } from '../../utils' -import { ExecutionController } from '../../controllers' +import { getWebBuildFolderPath } from '../../utils' const webRouter = express.Router() diff --git a/api/src/utils/validation.ts b/api/src/utils/validation.ts index 7113ed1..3aff19f 100644 --- a/api/src/utils/validation.ts +++ b/api/src/utils/validation.ts @@ -76,3 +76,10 @@ export const updateFileDriveValidation = (data: any): Joi.ValidationResult => filePath: Joi.string().required(), fileContent: Joi.string().required() }).validate(data) + +export const executeProgramRawValidation = (data: any): Joi.ValidationResult => + Joi.object({ + _program: Joi.string().required + }) + .pattern(/\w\d/, Joi.string()) + .validate(data) diff --git a/api/tsoa.json b/api/tsoa.json index fffd20d..6bb9d1a 100644 --- a/api/tsoa.json +++ b/api/tsoa.json @@ -30,6 +30,10 @@ { "name": "Group", "description": "Operations about group" + }, + { + "name": "STP", + "description": "Operations about STP" } ], "yaml": true, diff --git a/public/swagger.yaml b/public/swagger.yaml deleted file mode 100644 index cad804e..0000000 --- a/public/swagger.yaml +++ /dev/null @@ -1,782 +0,0 @@ -components: - examples: {} - headers: {} - parameters: {} - requestBodies: {} - responses: {} - schemas: - MemberType.folder: - enum: - - folder - type: string - FolderMember: - properties: - name: - type: string - type: - $ref: '#/components/schemas/MemberType.folder' - members: - items: - anyOf: - - - $ref: '#/components/schemas/FolderMember' - - - $ref: '#/components/schemas/ServiceMember' - type: array - required: - - name - - type - - members - type: object - additionalProperties: false - MemberType.service: - enum: - - service - type: string - ServiceMember: - properties: - name: - type: string - type: - $ref: '#/components/schemas/MemberType.service' - code: - type: string - required: - - name - - type - - code - type: object - additionalProperties: false - FileTree: - properties: - members: - items: - anyOf: - - - $ref: '#/components/schemas/FolderMember' - - - $ref: '#/components/schemas/ServiceMember' - type: array - required: - - members - type: object - additionalProperties: false - DeployResponse: - properties: - status: - type: string - message: - type: string - example: - $ref: '#/components/schemas/FileTree' - required: - - status - - message - type: object - additionalProperties: false - DeployPayload: - properties: - appLoc: - type: string - fileTree: - $ref: '#/components/schemas/FileTree' - required: - - fileTree - type: object - additionalProperties: false - UserResponse: - properties: - id: - type: number - format: double - username: - type: string - displayName: - type: string - required: - - id - - username - - displayName - type: object - additionalProperties: false - UserDetailsResponse: - properties: - id: - type: number - format: double - displayName: - type: string - username: - type: string - isActive: - type: boolean - isAdmin: - type: boolean - required: - - id - - displayName - - username - - isActive - - isAdmin - type: object - additionalProperties: false - UserPayload: - properties: - displayName: - type: string - description: 'Display name for user' - example: 'John Snow' - username: - type: string - description: 'Username for user' - example: johnSnow01 - password: - type: string - description: 'Password for user' - isAdmin: - type: boolean - description: 'Account should be admin or not, defaults to false' - example: 'false' - isActive: - type: boolean - description: 'Account should be active or not, defaults to true' - example: 'true' - required: - - displayName - - username - - password - type: object - additionalProperties: false - GroupResponse: - properties: - groupId: - type: number - format: double - name: - type: string - description: - type: string - required: - - groupId - - name - - description - type: object - additionalProperties: false - GroupDetailsResponse: - properties: - groupId: - type: number - format: double - name: - type: string - description: - type: string - isActive: - type: boolean - users: - items: - $ref: '#/components/schemas/UserResponse' - type: array - required: - - groupId - - name - - description - - isActive - - users - type: object - additionalProperties: false - GroupPayload: - properties: - name: - type: string - description: 'Name of the group' - example: DCGroup - description: - type: string - description: 'Description of the group' - example: 'This group represents Data Controller Users' - isActive: - type: boolean - description: 'Group should be active or not, defaults to true' - example: 'true' - required: - - name - - description - type: object - additionalProperties: false - ClientPayload: - properties: - clientId: - type: string - description: 'Client ID' - example: someFormattedClientID1234 - clientSecret: - type: string - description: 'Client Secret' - example: someRandomCryptoString - required: - - clientId - - clientSecret - type: object - additionalProperties: false - AuthorizeResponse: - properties: - code: - type: string - description: 'Authorization code' - example: someRandomCryptoString - required: - - code - type: object - additionalProperties: false - AuthorizePayload: - properties: - username: - type: string - description: 'Username for user' - example: secretuser - password: - type: string - description: 'Password for user' - example: secretpassword - clientId: - type: string - description: 'Client ID' - example: clientID1 - required: - - username - - password - - clientId - type: object - additionalProperties: false - TokenResponse: - properties: - accessToken: - type: string - description: 'Access Token' - example: someRandomCryptoString - refreshToken: - type: string - description: 'Refresh Token' - example: someRandomCryptoString - required: - - accessToken - - refreshToken - type: object - additionalProperties: false - TokenPayload: - properties: - clientId: - type: string - description: 'Client ID' - example: clientID1 - code: - type: string - description: 'Authorization code' - example: someRandomCryptoString - required: - - clientId - - code - type: object - additionalProperties: false - InfoJWT: - properties: - clientId: - type: string - userId: - type: number - format: double - required: - - clientId - - userId - type: object - additionalProperties: false - securitySchemes: - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT -info: - title: server - version: 0.0.1 - description: 'SASjs server' - contact: - name: 'Analytium Ltd' -openapi: 3.0.0 -paths: - /SASjsApi/drive/deploy: - post: - operationId: Deploy - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/DeployResponse' - examples: - 'Example 1': - value: {status: success, message: 'Files deployed successfully to @sasjs/server.'} - '400': - description: 'Invalid Format' - content: - application/json: - schema: - $ref: '#/components/schemas/DeployResponse' - examples: - 'Example 1': - value: {status: failure, message: 'Provided not supported data format.'} - '500': - description: 'Execution Error' - content: - application/json: - schema: - $ref: '#/components/schemas/DeployResponse' - examples: - 'Example 1': - value: {status: failure, message: 'Deployment failed!'} - description: 'Creates/updates files within SASjs Drive using provided payload.' - tags: - - Drive - security: - - - bearerAuth: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/DeployPayload' - /SASjsApi/user: - get: - operationId: GetAllUsers - responses: - '200': - description: Ok - content: - application/json: - schema: - items: - $ref: '#/components/schemas/UserResponse' - type: array - examples: - 'Example 1': - value: [{id: 123, username: johnusername, displayName: John}, {id: 456, username: starkusername, displayName: Stark}] - description: 'Get list of all users (username, displayname). All users can request this.' - tags: - - User - security: - - - bearerAuth: [] - parameters: [] - post: - operationId: CreateUser - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/UserDetailsResponse' - examples: - 'Example 1': - value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true} - description: 'Create user with the following attributes: UserId, UserName, Password, isAdmin, isActive. Admin only task.' - tags: - - User - security: - - - bearerAuth: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UserPayload' - '/SASjsApi/user/{userId}': - get: - operationId: GetUser - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/UserDetailsResponse' - description: 'Get user properties - such as group memberships, userName, displayName.' - tags: - - User - security: - - - bearerAuth: [] - parameters: - - - description: 'The user''s identifier' - in: path - name: userId - required: true - schema: - format: double - type: number - example: 1234 - patch: - operationId: UpdateUser - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/UserDetailsResponse' - examples: - 'Example 1': - value: {id: 1234, displayName: 'John Snow', username: johnSnow01, isAdmin: false, isActive: true} - description: 'Update user properties - such as displayName. Can be performed either by admins, or the user in question.' - tags: - - User - security: - - - bearerAuth: [] - parameters: - - - description: 'The user''s identifier' - in: path - name: userId - required: true - schema: - format: double - type: number - example: '1234' - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UserPayload' - delete: - operationId: DeleteUser - responses: - '204': - description: 'No content' - description: 'Delete a user. Can be performed either by admins, or the user in question.' - tags: - - User - security: - - - bearerAuth: [] - parameters: - - - description: 'The user''s identifier' - in: path - name: userId - required: true - schema: - format: double - type: number - example: 1234 - requestBody: - required: true - content: - application/json: - schema: - properties: - password: - type: string - type: object - /SASjsApi/group: - get: - operationId: GetAllGroups - responses: - '200': - description: Ok - content: - application/json: - schema: - items: - $ref: '#/components/schemas/GroupResponse' - type: array - examples: - 'Example 1': - value: [{groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users'}] - description: 'Get list of all groups (groupName and groupDescription). All users can request this.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: [] - post: - operationId: CreateGroup - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/GroupDetailsResponse' - examples: - 'Example 1': - value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Create a new group. Admin only.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/GroupPayload' - '/SASjsApi/group/{groupId}': - get: - operationId: GetGroup - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/GroupDetailsResponse' - description: 'Get list of members of a group (userName). All users can request this.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: - - - description: 'The group''s identifier' - in: path - name: groupId - required: true - schema: - format: double - type: number - example: 1234 - delete: - operationId: DeleteGroup - responses: - '204': - description: 'No content' - description: 'Delete a group. Admin task only.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: - - - description: 'The group''s identifier' - in: path - name: groupId - required: true - schema: - format: double - type: number - example: 1234 - '/SASjsApi/group/{groupId}/{userId}': - post: - operationId: AddUserToGroup - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/GroupDetailsResponse' - examples: - 'Example 1': - value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Add a user to a group. Admin task only.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: - - - description: 'The group''s identifier' - in: path - name: groupId - required: true - schema: - format: double - type: number - example: '1234' - - - description: 'The user''s identifier' - in: path - name: userId - required: true - schema: - format: double - type: number - example: '6789' - delete: - operationId: RemoveUserFromGroup - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/GroupDetailsResponse' - examples: - 'Example 1': - value: {groupId: 123, name: DCGroup, description: 'This group represents Data Controller Users', isActive: true, users: []} - description: 'Remove a user to a group. Admin task only.' - tags: - - Group - security: - - - bearerAuth: [] - parameters: - - - description: 'The group''s identifier' - in: path - name: groupId - required: true - schema: - format: double - type: number - example: '1234' - - - description: 'The user''s identifier' - in: path - name: userId - required: true - schema: - format: double - type: number - example: '6789' - /SASjsApi/client: - post: - operationId: CreateClient - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/ClientPayload' - examples: - 'Example 1': - value: {clientId: someFormattedClientID1234, clientSecret: someRandomCryptoString} - description: 'Create client with the following attributes: ClientId, ClientSecret. Admin only task.' - tags: - - Client - security: - - - bearerAuth: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ClientPayload' - /SASjsApi/auth/authorize: - post: - operationId: Authorize - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/AuthorizeResponse' - examples: - 'Example 1': - value: {code: someRandomCryptoString} - description: 'Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE' - tags: - - Auth - security: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/AuthorizePayload' - /SASjsApi/auth/token: - post: - operationId: Token - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/TokenResponse' - examples: - 'Example 1': - value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString} - description: 'Accepts client/auth code and returns access/refresh tokens' - tags: - - Auth - security: [] - parameters: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TokenPayload' - /SASjsApi/auth/refresh: - post: - operationId: Refresh - responses: - '200': - description: Ok - content: - application/json: - schema: - $ref: '#/components/schemas/TokenResponse' - examples: - 'Example 1': - value: {accessToken: someRandomCryptoString, refreshToken: someRandomCryptoString} - description: 'Returns new access/refresh tokens' - tags: - - Auth - security: - - - bearerAuth: [] - parameters: [] - /SASjsApi/auth/logout: - post: - operationId: Logout - responses: - '204': - description: 'No content' - description: 'Logout terminate access/refresh tokens and returns nothing' - tags: - - Auth - security: - - - bearerAuth: [] - parameters: [] -servers: - - - url: / -tags: - - - name: User - description: 'Operations about users' - - - name: Client - description: 'Operations about clients' - - - name: Auth - description: 'Operations about auth' - - - name: Drive - description: 'Operations about drive' - - - name: Group - description: 'Operations about group' diff --git a/web/.env.example b/web/.env.example index 5e4cda4..94ecfa6 100644 --- a/web/.env.example +++ b/web/.env.example @@ -1 +1 @@ -CLIENT_ID= \ No newline at end of file +REACT_APP_CLIENT_ID= \ No newline at end of file