1
0
mirror of https://github.com/sasjs/server.git synced 2026-01-06 22:20:06 +00:00

chore: drive all endpoints docs generated

This commit is contained in:
Saad Jutt
2021-11-10 21:59:46 +05:00
parent 31959455c3
commit 45fbf2df46
5 changed files with 349 additions and 63 deletions

View File

@@ -84,6 +84,73 @@ components:
- fileTree - fileTree
type: object type: object
additionalProperties: false additionalProperties: false
GetFileResponse:
properties:
status:
type: string
fileContent:
type: string
message:
type: string
required:
- status
type: object
additionalProperties: false
UpdateFileResponse:
properties:
status:
type: string
message:
type: string
required:
- status
type: object
additionalProperties: false
FilePayload:
properties:
filePath:
type: string
description: 'Path of the file'
example: /Public/somefolder/some.file
fileContent:
type: string
description: 'Contents of the file'
example: 'Contents of the File'
required:
- filePath
- fileContent
type: object
additionalProperties: false
TreeNode:
properties:
name:
type: string
relativePath:
type: string
absolutePath:
type: string
children:
items:
$ref: '#/components/schemas/TreeNode'
type: array
required:
- name
- relativePath
- absolutePath
- children
type: object
additionalProperties: false
GetFileTreeResponse:
properties:
status:
type: string
tree:
$ref: '#/components/schemas/TreeNode'
required:
- status
- tree
type: object
additionalProperties: false
UserResponse: UserResponse:
properties: properties:
id: id:
@@ -348,6 +415,92 @@ paths:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/DeployPayload' $ref: '#/components/schemas/DeployPayload'
/SASjsApi/drive/file:
get:
operationId: GetFile
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/GetFileResponse'
examples:
'Example 1':
value: {status: success, fileContent: 'Contents of the File'}
'400':
description: 'Unable to get File'
content:
application/json:
schema:
$ref: '#/components/schemas/GetFileResponse'
examples:
'Example 1':
value: {status: failure, message: 'File request failed.'}
description: 'Get file from SASjs Drive'
tags:
- Drive
security:
-
bearerAuth: []
parameters:
-
in: query
name: filePath
required: true
schema:
type: string
patch:
operationId: UpdateFile
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateFileResponse'
examples:
'Example 1':
value: {status: success}
'400':
description: 'Unable to get File'
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateFileResponse'
examples:
'Example 1':
value: {status: failure, message: 'File request failed.'}
description: 'Modify a file in SASjs Drive'
tags:
- Drive
security:
-
bearerAuth: []
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FilePayload'
/SASjsApi/drive/filetree:
get:
operationId: GetFileTree
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/GetFileTreeResponse'
description: 'Fetch file tree within SASjs Drive.'
tags:
- Drive
security:
-
bearerAuth: []
parameters: []
/SASjsApi/user: /SASjsApi/user:
get: get:
operationId: GetAllUsers operationId: GetAllUsers

View File

@@ -1,13 +1,38 @@
import { Security, Route, Tags, Example, Post, Body, Response } from 'tsoa' import {
Security,
Route,
Tags,
Example,
Post,
Body,
Response,
Query,
Get,
Patch
} from 'tsoa'
import { fileExists, readFile, createFile } from '@sasjs/utils' import { fileExists, readFile, createFile } from '@sasjs/utils'
import { createFileTree, getTreeExample } from '.' import { createFileTree, ExecutionController, getTreeExample } from '.'
import { FileTree, isFileTree } from '../types' import { FileTree, isFileQuery, isFileTree, TreeNode } from '../types'
import path from 'path'
import { getTmpFilesFolderPath } from '../utils'
interface DeployPayload { interface DeployPayload {
appLoc?: string appLoc?: string
fileTree: FileTree fileTree: FileTree
} }
interface FilePayload {
/**
* Path of the file
* @example "/Public/somefolder/some.file"
*/
filePath: string
/**
* Contents of the file
* @example "Contents of the File"
*/
fileContent: string
}
interface DeployResponse { interface DeployResponse {
status: string status: string
@@ -15,18 +40,34 @@ interface DeployResponse {
example?: FileTree example?: FileTree
} }
interface GetFileResponse {
status: string
fileContent?: string
message?: string
}
interface GetFileTreeResponse {
status: string
tree: TreeNode
}
interface UpdateFileResponse {
status: string
message?: string
}
const fileTreeExample = getTreeExample() const fileTreeExample = getTreeExample()
const successResponse: DeployResponse = { const successDeployResponse: DeployResponse = {
status: 'success', status: 'success',
message: 'Files deployed successfully to @sasjs/server.' message: 'Files deployed successfully to @sasjs/server.'
} }
const invalidFormatResponse: DeployResponse = { const invalidDeployFormatResponse: DeployResponse = {
status: 'failure', status: 'failure',
message: 'Provided not supported data format.', message: 'Provided not supported data format.',
example: fileTreeExample example: fileTreeExample
} }
const execErrorResponse: DeployResponse = { const execDeployErrorResponse: DeployResponse = {
status: 'failure', status: 'failure',
message: 'Deployment failed!' message: 'Deployment failed!'
} }
@@ -39,42 +80,122 @@ export class DriveController {
* Creates/updates files within SASjs Drive using provided payload. * Creates/updates files within SASjs Drive using provided payload.
* *
*/ */
@Example<DeployResponse>(successResponse) @Example<DeployResponse>(successDeployResponse)
@Response<DeployResponse>(400, 'Invalid Format', invalidFormatResponse) @Response<DeployResponse>(400, 'Invalid Format', invalidDeployFormatResponse)
@Response<DeployResponse>(500, 'Execution Error', execErrorResponse) @Response<DeployResponse>(500, 'Execution Error', execDeployErrorResponse)
@Post('/deploy') @Post('/deploy')
public async deploy(@Body() body: DeployPayload): Promise<DeployResponse> { public async deploy(@Body() body: DeployPayload): Promise<DeployResponse> {
return deploy(body) return deploy(body)
} }
async readFile(filePath: string) { /**
await this.validateFilePath(filePath) * Get file from SASjs Drive
return await readFile(filePath) *
*/
@Example<GetFileResponse>({
status: 'success',
fileContent: 'Contents of the File'
})
@Response<GetFileResponse>(400, 'Unable to get File', {
status: 'failure',
message: 'File request failed.'
})
@Get('/file')
public async getFile(@Query() filePath: string): Promise<GetFileResponse> {
return getFile(filePath)
} }
async updateFile(filePath: string, fileContent: string) { /**
await this.validateFilePath(filePath) * Modify a file in SASjs Drive
return await createFile(filePath, fileContent) *
*/
@Example<UpdateFileResponse>({
status: 'success'
})
@Response<UpdateFileResponse>(400, 'Unable to get File', {
status: 'failure',
message: 'File request failed.'
})
@Patch('/file')
public async updateFile(
@Body() body: FilePayload
): Promise<UpdateFileResponse> {
return updateFile(body)
} }
private async validateFilePath(filePath: string) { /**
if (!(await fileExists(filePath))) { * Fetch file tree within SASjs Drive.
throw 'DriveController: File does not exists.' *
} */
@Get('/filetree')
public async getFileTree(): Promise<GetFileTreeResponse> {
return getFileTree()
} }
} }
const getFileTree = () => {
const tree = new ExecutionController().buildDirectorytree()
return { status: 'success', tree }
}
const deploy = async (data: DeployPayload) => { const deploy = async (data: DeployPayload) => {
if (!isFileTree(data.fileTree)) { if (!isFileTree(data.fileTree)) {
throw { code: 400, ...invalidFormatResponse } throw { code: 400, ...invalidDeployFormatResponse }
} }
await createFileTree( await createFileTree(
data.fileTree.members, data.fileTree.members,
data.appLoc ? data.appLoc.replace(/^\//, '').split('/') : [] data.appLoc ? data.appLoc.replace(/^\//, '').split('/') : []
).catch((err) => { ).catch((err) => {
throw { code: 500, ...execErrorResponse, ...err } throw { code: 500, ...execDeployErrorResponse, ...err }
}) })
return successResponse return successDeployResponse
}
const getFile = async (filePath: string): Promise<GetFileResponse> => {
try {
const filePathFull = path
.join(getTmpFilesFolderPath(), filePath)
.replace(new RegExp('/', 'g'), path.sep)
await validateFilePath(filePathFull)
const fileContent = await readFile(filePathFull)
return { status: 'success', fileContent: fileContent }
} catch (err) {
throw {
code: 400,
status: 'failure',
message: 'File request failed.',
...(typeof err === 'object' ? err : { details: err })
}
}
}
const updateFile = async (body: FilePayload): Promise<GetFileResponse> => {
const { filePath, fileContent } = body
try {
const filePathFull = path
.join(getTmpFilesFolderPath(), filePath)
.replace(new RegExp('/', 'g'), path.sep)
await validateFilePath(filePathFull)
await createFile(filePathFull, fileContent)
return { status: 'success' }
} catch (err) {
throw {
code: 400,
status: 'failure',
message: 'File request failed.',
...(typeof err === 'object' ? err : { details: err })
}
}
}
const validateFilePath = async (filePath: string) => {
if (!(await fileExists(filePath))) {
throw 'DriveController: File does not exists.'
}
} }

View File

@@ -3,7 +3,11 @@ import path from 'path'
import { ExecutionController } from '../../controllers' import { ExecutionController } from '../../controllers'
import { DriveController } from '../../controllers/drive' import { DriveController } from '../../controllers/drive'
import { isFileQuery } from '../../types' import { isFileQuery } from '../../types'
import { getTmpFilesFolderPath } from '../../utils' import {
getFileDriveValidation,
getTmpFilesFolderPath,
updateFileDriveValidation
} from '../../utils'
const driveRouter = express.Router() const driveRouter = express.Router()
@@ -22,51 +26,47 @@ driveRouter.post('/deploy', async (req, res) => {
}) })
driveRouter.get('/file', async (req, res) => { driveRouter.get('/file', async (req, res) => {
if (isFileQuery(req.query)) { const { error, value: query } = getFileDriveValidation(req.query)
const filePath = path if (error) return res.status(400).send(error.details[0].message)
.join(getTmpFilesFolderPath(), req.query.filePath)
.replace(new RegExp('/', 'g'), path.sep) const controller = new DriveController()
await new DriveController() try {
.readFile(filePath) const response = await controller.getFile(query.filePath)
.then((fileContent) => { res.send(response)
res.status(200).send({ status: 'success', fileContent: fileContent }) } catch (err: any) {
}) const statusCode = err.code
.catch((err) => {
res.status(400).send({ delete err.code
status: 'failure',
message: 'File request failed.', res.status(statusCode).send(err)
...(typeof err === 'object' ? err : { details: err })
})
})
} else {
res.status(400).send({
status: 'failure',
message: 'Invalid Request: Expected parameter filePath was not provided'
})
} }
}) })
driveRouter.patch('/file', async (req, res) => { driveRouter.patch('/file', async (req, res) => {
const filePath = path const { error, value: body } = updateFileDriveValidation(req.body)
.join(getTmpFilesFolderPath(), req.body.filePath) if (error) return res.status(400).send(error.details[0].message)
.replace(new RegExp('/', 'g'), path.sep)
await new DriveController() const controller = new DriveController()
.updateFile(filePath, req.body.fileContent) try {
.then(() => { const response = await controller.updateFile(body)
res.status(200).send({ status: 'success' }) res.send(response)
}) } catch (err: any) {
.catch((err) => { const statusCode = err.code
res.status(400).send({
status: 'failure', delete err.code
message: 'File request failed.',
...(typeof err === 'object' ? err : { details: err }) res.status(statusCode).send(err)
}) }
})
}) })
driveRouter.get('/fileTree', async (req, res) => { driveRouter.get('/fileTree', async (req, res) => {
const tree = new ExecutionController().buildDirectorytree() const controller = new DriveController()
res.status(200).send({ status: 'success', tree }) try {
const response = await controller.getFileTree()
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
}) })
export default driveRouter export default driveRouter

View File

@@ -65,3 +65,14 @@ export const registerClientValidation = (data: any): Joi.ValidationResult =>
clientId: Joi.string().required(), clientId: Joi.string().required(),
clientSecret: Joi.string().required() clientSecret: Joi.string().required()
}).validate(data) }).validate(data)
export const getFileDriveValidation = (data: any): Joi.ValidationResult =>
Joi.object({
filePath: Joi.string().required()
}).validate(data)
export const updateFileDriveValidation = (data: any): Joi.ValidationResult =>
Joi.object({
filePath: Joi.string().required(),
fileContent: Joi.string().required()
}).validate(data)

View File

@@ -3,8 +3,9 @@
"version": "0.0.1", "version": "0.0.1",
"description": "NodeJS wrapper for calling the SAS binary executable", "description": "NodeJS wrapper for calling the SAS binary executable",
"scripts": { "scripts": {
"server:install": "cd web && npm ci && cd ../api && npm ci", "server": "npm run server:prepare && npm run server:start",
"server": "cd web && npm run build && cd ../api && npm run start:prod", "server:prepare": "cd web && npm ci && npm run build && cd ../api && npm ci && cd ..",
"server:start": "cd api && npm run start:prod",
"lint-api:fix": "npx prettier --write \"api/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "lint-api:fix": "npx prettier --write \"api/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
"lint-api": "npx prettier --check \"api/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "lint-api": "npx prettier --check \"api/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",
"lint-web:fix": "npx prettier --write \"web/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"", "lint-web:fix": "npx prettier --write \"web/src/**/*.{ts,tsx,js,jsx,html,css,sass,less,yml,md,graphql}\"",