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:
@@ -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
|
||||||
|
|||||||
@@ -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.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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}\"",
|
||||||
|
|||||||
Reference in New Issue
Block a user