From b4bf72f70401a81b6d5d0104332a1fbc5f71562b Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Fri, 9 Jul 2021 08:27:15 +0300 Subject: [PATCH] feat(deploy): add route to deploy a file tree to @sasjs/server --- src/controllers/deploy.ts | 59 +++++++++++++++++++++++++++++++++++++++ src/controllers/index.ts | 48 ++----------------------------- src/routes/index.ts | 20 +++++++++++-- src/types/fileTree.ts | 47 +++++++++++++++++++++++++++++++ src/types/index.ts | 1 + src/utils/file.ts | 8 ++++++ 6 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 src/controllers/deploy.ts create mode 100644 src/types/fileTree.ts create mode 100644 src/utils/file.ts diff --git a/src/controllers/deploy.ts b/src/controllers/deploy.ts new file mode 100644 index 0000000..9d8c44d --- /dev/null +++ b/src/controllers/deploy.ts @@ -0,0 +1,59 @@ +import { MemberType, FolderMember, ServiceMember } from '../types' +import { getTmpFilesFolderPath } from '../utils/file' +import { createFolder, createFile, asyncForEach } from '@sasjs/utils' +import path from 'path' + +export const createFileTree = async ( + members: [FolderMember, ServiceMember], + parentFolders: string[] = [] +) => { + const destinationPath = path.join( + getTmpFilesFolderPath(), + path.join(...parentFolders) + ) + + await asyncForEach(members, async (member: FolderMember | ServiceMember) => { + const name = member.name + + if (member.type === MemberType.folder) { + await createFolder(path.join(destinationPath, name)).catch((err) => + Promise.reject({ error: err, failedToCreate: name }) + ) + + await createFileTree(member.members, [...parentFolders, name]).catch( + (err) => Promise.reject({ error: err, failedToCreate: name }) + ) + } else { + await createFile(path.join(destinationPath, name), member.code).catch( + (err) => Promise.reject({ error: err, failedToCreate: name }) + ) + } + }) + + return Promise.resolve() +} + +export const getTreeExample = () => ({ + message: 'Provided not supported data format.', + supportedFormat: { + members: [ + { + name: 'jobs', + type: 'folder', + members: [ + { + name: 'extract', + type: 'folder', + members: [ + { + name: 'makedata1', + type: 'service', + code: '%put Hello World!;' + } + ] + } + ] + } + ] + } +}) diff --git a/src/controllers/index.ts b/src/controllers/index.ts index 3529901..e90f306 100644 --- a/src/controllers/index.ts +++ b/src/controllers/index.ts @@ -1,46 +1,2 @@ -import { execFile } from 'child_process' -import { readFile, generateTimestamp, deleteFile } from '@sasjs/utils' -import path from 'path' -import { ExecutionResult, RequestQuery } from '../types' - -// FIXME -const sasExePath = `C:\\Program Files\\SASHome\\SASFoundation\\9.4\\sas.exe` -const baseSasLogPath = 'C:\\Users\\YuryShkoda\\projects\\server\\sas\\logs' -const baseSasCodePath = `sas` - -// TODO: create utils isSasFile - -export const processSas = async ( - query: RequestQuery -): Promise => - new Promise((resolve, reject) => { - let sasCodePath = query._program - sasCodePath = path.join(baseSasCodePath, `${sasCodePath}.sas`) - sasCodePath = sasCodePath.replace(new RegExp('/', 'g'), path.sep) - - const sasFile: string = sasCodePath.split(path.sep).pop() || 'default' - - const sasLogPath = [ - baseSasLogPath, - path.sep, - sasFile.replace(/\.sas/g, ''), - '-', - generateTimestamp(), - '.log' - ].join('') - - execFile( - sasExePath, - ['-SYSIN', sasCodePath, '-log', sasLogPath, '-nosplash'], - async (err, _, stderr) => { - if (err) reject(err) - if (stderr) reject(stderr) - - const log = await readFile(sasLogPath) - - deleteFile(sasLogPath) - - resolve({ log: log, logPath: sasLogPath }) - } - ) - }) +export * from './sas' +export * from './deploy' diff --git a/src/routes/index.ts b/src/routes/index.ts index bf8af62..9191b7a 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -1,6 +1,6 @@ import express from 'express' -import { processSas } from '../controllers' -import { ExecutionResult, RequestQuery, isRequestQuery } from '../types' +import { processSas, createFileTree, getTreeExample } from '../controllers' +import { ExecutionResult, isRequestQuery, isFileTree } from '../types' const router = express.Router() @@ -20,4 +20,20 @@ router.get('/', async (req, res) => {

Log:

`) }) +router.post('/deploy', async (req, res) => { + if (!isFileTree(req.body)) { + res.status(400).send(getTreeExample()) + + return + } + + await createFileTree(req.body.members) + .then(() => { + res.status(200).send('Files deployed successfully to @sasjs/server.') + }) + .catch((err) => { + res.status(500).send({ message: 'Deployment failed!', ...err }) + }) +}) + export default router diff --git a/src/types/fileTree.ts b/src/types/fileTree.ts new file mode 100644 index 0000000..548bc5a --- /dev/null +++ b/src/types/fileTree.ts @@ -0,0 +1,47 @@ +export interface FileTree { + members: [FolderMember, ServiceMember] +} + +export enum MemberType { + folder = 'folder', + service = 'service' +} + +export interface FolderMember { + name: string + type: MemberType.folder + members: [FolderMember, ServiceMember] +} + +export interface ServiceMember { + name: string + type: MemberType.service + code: string +} + +export const isFileTree = (arg: any): arg is FileTree => + arg && + arg.members && + Array.isArray(arg.members) && + arg.members.filter( + (member: FolderMember | ServiceMember) => + !isFolderMember(member) && !isServiceMember(member) + ).length === 0 + +const isFolderMember = (arg: any): arg is FolderMember => + arg && + typeof arg.name === 'string' && + arg.type === MemberType.folder && + arg.members && + Array.isArray(arg.members) && + arg.members.filter( + (member: FolderMember | ServiceMember) => + !isFolderMember(member) && !isServiceMember(member) + ).length === 0 + +const isServiceMember = (arg: any): arg is ServiceMember => + arg && + typeof arg.name === 'string' && + arg.type === MemberType.service && + arg.code && + typeof arg.code === 'string' diff --git a/src/types/index.ts b/src/types/index.ts index 51251cc..7897c28 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,2 +1,3 @@ export * from './sas' export * from './request' +export * from './fileTree' diff --git a/src/utils/file.ts b/src/utils/file.ts new file mode 100644 index 0000000..163dd0d --- /dev/null +++ b/src/utils/file.ts @@ -0,0 +1,8 @@ +import path from 'path' +import { getRealPath } from '@sasjs/utils' + +export const getTmpFolderPath = () => + getRealPath(path.join(__dirname, '..', '..', 'tmp')) + +export const getTmpFilesFolderPath = () => + path.join(getTmpFolderPath(), 'files')