mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 03:34:35 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99fb5f4b2b | ||
|
|
5dc3deeb11 | ||
|
|
6b708fcad3 | ||
|
|
bc0ff84d8d | ||
|
|
1ff6965dd2 | ||
|
|
d6fa877941 | ||
|
|
940f705f5d | ||
|
|
7a6e6c8bec |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.0.33](https://github.com/sasjs/server/compare/v0.0.32...v0.0.33) (2022-03-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* serve deployed streaming apps ([d6fa877](https://github.com/sasjs/server/commit/d6fa87794155880adc23c2552c37c86ad606c292))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* adde validation + code improvement ([1ff6965](https://github.com/sasjs/server/commit/1ff6965dd2f44ad74136af04b4fba8c76979ecba))
|
||||||
|
* added api button on web component ([6b708fc](https://github.com/sasjs/server/commit/6b708fcad30d92c21713f9c97bca173c148cc875))
|
||||||
|
|
||||||
|
### [0.0.32](https://github.com/sasjs/server/compare/v0.0.31...v0.0.32) (2022-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **web:** added delete option in Drive ([7a6e6c8](https://github.com/sasjs/server/commit/7a6e6c8becab31410d0a36bcc22e13d5359a6cdf))
|
||||||
|
|
||||||
### [0.0.31](https://github.com/sasjs/server/compare/v0.0.30...v0.0.31) (2022-03-14)
|
### [0.0.31](https://github.com/sasjs/server/compare/v0.0.30...v0.0.31) (2022-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
api/.nvmrc
Normal file
1
api/.nvmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
v16.14.0
|
||||||
408
api/package-lock.json
generated
408
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -46,7 +46,7 @@
|
|||||||
"author": "4GL Ltd",
|
"author": "4GL Ltd",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/core": "4.9.0",
|
"@sasjs/core": "4.9.0",
|
||||||
"@sasjs/utils": "2.34.1",
|
"@sasjs/utils": "2.36.2",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@@ -57,8 +57,7 @@
|
|||||||
"mongoose-sequence": "^5.3.1",
|
"mongoose-sequence": "^5.3.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"multer": "^1.4.3",
|
"multer": "^1.4.3",
|
||||||
"swagger-ui-express": "^4.1.6",
|
"swagger-ui-express": "^4.1.6"
|
||||||
"tsoa": "3.14.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bcryptjs": "^2.4.2",
|
"@types/bcryptjs": "^2.4.2",
|
||||||
@@ -84,6 +83,7 @@
|
|||||||
"supertest": "^6.1.3",
|
"supertest": "^6.1.3",
|
||||||
"ts-jest": "^27.0.3",
|
"ts-jest": "^27.0.3",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
|
"tsoa": "3.14.1",
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
},
|
},
|
||||||
"configuration": {
|
"configuration": {
|
||||||
|
|||||||
@@ -172,12 +172,20 @@ components:
|
|||||||
enum:
|
enum:
|
||||||
- service
|
- service
|
||||||
type: string
|
type: string
|
||||||
|
MemberType.file:
|
||||||
|
enum:
|
||||||
|
- file
|
||||||
|
type: string
|
||||||
ServiceMember:
|
ServiceMember:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
$ref: '#/components/schemas/MemberType.service'
|
anyOf:
|
||||||
|
-
|
||||||
|
$ref: '#/components/schemas/MemberType.service'
|
||||||
|
-
|
||||||
|
$ref: '#/components/schemas/MemberType.file'
|
||||||
code:
|
code:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
@@ -220,6 +228,7 @@ components:
|
|||||||
fileTree:
|
fileTree:
|
||||||
$ref: '#/components/schemas/FileTree'
|
$ref: '#/components/schemas/FileTree'
|
||||||
required:
|
required:
|
||||||
|
- appLoc
|
||||||
- fileTree
|
- fileTree
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const compiledSystemInit = async (systemInit: string) =>
|
|||||||
programFolders: [],
|
programFolders: [],
|
||||||
macroFolders: [],
|
macroFolders: [],
|
||||||
buildSourceFolder: '',
|
buildSourceFolder: '',
|
||||||
|
binaryFolders: [],
|
||||||
macroCorePath
|
macroCorePath
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { FileTree, isFileTree, TreeNode } from '../types'
|
|||||||
import { getTmpFilesFolderPath } from '../utils'
|
import { getTmpFilesFolderPath } from '../utils'
|
||||||
|
|
||||||
interface DeployPayload {
|
interface DeployPayload {
|
||||||
appLoc?: string
|
appLoc: string
|
||||||
fileTree: FileTree
|
fileTree: FileTree
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ const deploy = async (data: DeployPayload) => {
|
|||||||
|
|
||||||
await createFileTree(
|
await createFileTree(
|
||||||
data.fileTree.members,
|
data.fileTree.members,
|
||||||
data.appLoc ? data.appLoc.replace(/^\//, '').split('/') : []
|
data.appLoc.replace(/^\//, '').split('/')
|
||||||
).catch((err) => {
|
).catch((err) => {
|
||||||
throw { code: 500, ...execDeployErrorResponse, ...err }
|
throw { code: 500, ...execDeployErrorResponse, ...err }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import path from 'path'
|
||||||
import { MemberType, FolderMember, ServiceMember, FileTree } from '../../types'
|
import { MemberType, FolderMember, ServiceMember, FileTree } from '../../types'
|
||||||
import { getTmpFilesFolderPath } from '../../utils/file'
|
import { getTmpFilesFolderPath } from '../../utils/file'
|
||||||
import { createFolder, createFile, asyncForEach } from '@sasjs/utils'
|
import { createFolder, createFile, asyncForEach } from '@sasjs/utils'
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
// REFACTOR: export FileTreeCpntroller
|
// REFACTOR: export FileTreeCpntroller
|
||||||
export const createFileTree = async (
|
export const createFileTree = async (
|
||||||
@@ -27,9 +27,13 @@ export const createFileTree = async (
|
|||||||
(err) => Promise.reject({ error: err, failedToCreate: name })
|
(err) => Promise.reject({ error: err, failedToCreate: name })
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
await createFile(path.join(destinationPath, name), member.code).catch(
|
const encoding = member.type === MemberType.file ? 'base64' : undefined
|
||||||
(err) => Promise.reject({ error: err, failedToCreate: name })
|
|
||||||
)
|
await createFile(
|
||||||
|
path.join(destinationPath, name),
|
||||||
|
member.code,
|
||||||
|
encoding
|
||||||
|
).catch((err) => Promise.reject({ error: err, failedToCreate: name }))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { deleteFile } from '@sasjs/utils'
|
import { deleteFile } from '@sasjs/utils'
|
||||||
|
|
||||||
|
import { publishAppStream } from '../appStream'
|
||||||
|
|
||||||
import { multerSingle } from '../../middlewares/multer'
|
import { multerSingle } from '../../middlewares/multer'
|
||||||
import { DriveController } from '../../controllers/'
|
import { DriveController } from '../../controllers/'
|
||||||
import { fileBodyValidation, fileParamValidation } from '../../utils'
|
import {
|
||||||
|
deployValidation,
|
||||||
|
fileBodyValidation,
|
||||||
|
fileParamValidation
|
||||||
|
} from '../../utils'
|
||||||
|
|
||||||
const controller = new DriveController()
|
const controller = new DriveController()
|
||||||
|
|
||||||
const driveRouter = express.Router()
|
const driveRouter = express.Router()
|
||||||
|
|
||||||
driveRouter.post('/deploy', async (req, res) => {
|
driveRouter.post('/deploy', async (req, res) => {
|
||||||
|
const { error, value: body } = deployValidation(req.body)
|
||||||
|
if (error) return res.status(400).send(error.details[0].message)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await controller.deploy(req.body)
|
const response = await controller.deploy(body)
|
||||||
|
|
||||||
|
const appLoc = body.appLoc.replace(/^\//, '')?.split('/')
|
||||||
|
|
||||||
|
publishAppStream(appLoc)
|
||||||
|
|
||||||
res.send(response)
|
res.send(response)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const statusCode = err.code
|
const statusCode = err.code
|
||||||
|
|||||||
@@ -74,14 +74,19 @@ describe('files', () => {
|
|||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/drive/deploy')
|
.post('/SASjsApi/drive/deploy')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send(payload)
|
.send({ appLoc: '/Public', fileTree: payload })
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(400)
|
expect(res.statusCode).toEqual(400)
|
||||||
expect(res.body).toEqual({
|
|
||||||
status: 'failure',
|
if (payload === undefined) {
|
||||||
message: 'Provided not supported data format.',
|
expect(res.text).toEqual('"fileTree" is required')
|
||||||
example: getTreeExample()
|
} else {
|
||||||
})
|
expect(res.body).toEqual({
|
||||||
|
status: 'failure',
|
||||||
|
message: 'Provided not supported data format.',
|
||||||
|
example: getTreeExample()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should respond with payload example if valid payload was not provided', async () => {
|
it('should respond with payload example if valid payload was not provided', async () => {
|
||||||
@@ -140,11 +145,11 @@ describe('files', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should respond with payload example if valid payload was not provided', async () => {
|
it('should successfully deploy if valid payload was provided', async () => {
|
||||||
const res = await request(app)
|
const res = await request(app)
|
||||||
.post('/SASjsApi/drive/deploy')
|
.post('/SASjsApi/drive/deploy')
|
||||||
.auth(accessToken, { type: 'bearer' })
|
.auth(accessToken, { type: 'bearer' })
|
||||||
.send({ fileTree: getTreeExample() })
|
.send({ appLoc: '/public', fileTree: getTreeExample() })
|
||||||
|
|
||||||
expect(res.statusCode).toEqual(200)
|
expect(res.statusCode).toEqual(200)
|
||||||
expect(res.text).toEqual(
|
expect(res.text).toEqual(
|
||||||
@@ -154,6 +159,7 @@ describe('files', () => {
|
|||||||
|
|
||||||
const testJobFolder = path.join(
|
const testJobFolder = path.join(
|
||||||
getTmpFilesFolderPath(),
|
getTmpFilesFolderPath(),
|
||||||
|
'public',
|
||||||
'jobs',
|
'jobs',
|
||||||
'extract'
|
'extract'
|
||||||
)
|
)
|
||||||
|
|||||||
26
api/src/routes/appStream/index.ts
Normal file
26
api/src/routes/appStream/index.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import path from 'path'
|
||||||
|
import express from 'express'
|
||||||
|
import { folderExists } from '@sasjs/utils'
|
||||||
|
|
||||||
|
import { getTmpFilesFolderPath } from '../../utils'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
export const publishAppStream = async (appLoc: string[]) => {
|
||||||
|
const appLocUrl = encodeURI(appLoc.join('/'))
|
||||||
|
const appLocPath = appLoc.join(path.sep)
|
||||||
|
|
||||||
|
const pathToDeployment = path.join(
|
||||||
|
getTmpFilesFolderPath(),
|
||||||
|
appLocPath,
|
||||||
|
'services',
|
||||||
|
'webv'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (await folderExists(pathToDeployment)) {
|
||||||
|
router.use(`/${appLocUrl}`, express.static(pathToDeployment))
|
||||||
|
console.log('Serving Stream App: ', appLocUrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default router
|
||||||
@@ -2,8 +2,15 @@ import { Express } from 'express'
|
|||||||
|
|
||||||
import webRouter from './web'
|
import webRouter from './web'
|
||||||
import apiRouter from './api'
|
import apiRouter from './api'
|
||||||
|
import appStreamRouter from './appStream'
|
||||||
|
|
||||||
export const setupRoutes = (app: Express) => {
|
export const setupRoutes = (app: Express) => {
|
||||||
app.use('/', webRouter)
|
app.use('/', webRouter)
|
||||||
app.use('/SASjsApi', apiRouter)
|
app.use('/SASjsApi', apiRouter)
|
||||||
|
|
||||||
|
app.use('/AppStream', function (req, res, next) {
|
||||||
|
// this needs to be a function to hook on
|
||||||
|
// whatever the current router is
|
||||||
|
appStreamRouter(req, res, next)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ export interface FileTree {
|
|||||||
|
|
||||||
export enum MemberType {
|
export enum MemberType {
|
||||||
folder = 'folder',
|
folder = 'folder',
|
||||||
service = 'service'
|
service = 'service',
|
||||||
|
file = 'file'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FolderMember {
|
export interface FolderMember {
|
||||||
@@ -15,7 +16,7 @@ export interface FolderMember {
|
|||||||
|
|
||||||
export interface ServiceMember {
|
export interface ServiceMember {
|
||||||
name: string
|
name: string
|
||||||
type: MemberType.service
|
type: MemberType.service | MemberType.file
|
||||||
code: string
|
code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +37,9 @@ const isFolderMember = (arg: any): arg is FolderMember =>
|
|||||||
Array.isArray(arg.members) &&
|
Array.isArray(arg.members) &&
|
||||||
arg.members.filter(
|
arg.members.filter(
|
||||||
(member: FolderMember | ServiceMember) =>
|
(member: FolderMember | ServiceMember) =>
|
||||||
!isFolderMember(member) && !isServiceMember(member)
|
!isFolderMember(member) &&
|
||||||
|
!isServiceMember(member) &&
|
||||||
|
!isFileMember(member)
|
||||||
).length === 0
|
).length === 0
|
||||||
|
|
||||||
const isServiceMember = (arg: any): arg is ServiceMember =>
|
const isServiceMember = (arg: any): arg is ServiceMember =>
|
||||||
@@ -45,3 +48,10 @@ const isServiceMember = (arg: any): arg is ServiceMember =>
|
|||||||
arg.type === MemberType.service &&
|
arg.type === MemberType.service &&
|
||||||
arg.code &&
|
arg.code &&
|
||||||
typeof arg.code === 'string'
|
typeof arg.code === 'string'
|
||||||
|
|
||||||
|
const isFileMember = (arg: any): arg is ServiceMember =>
|
||||||
|
arg &&
|
||||||
|
typeof arg.name === 'string' &&
|
||||||
|
arg.type === MemberType.file &&
|
||||||
|
arg.code &&
|
||||||
|
typeof arg.code === 'string'
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ export const registerClientValidation = (data: any): Joi.ValidationResult =>
|
|||||||
clientSecret: Joi.string().required()
|
clientSecret: Joi.string().required()
|
||||||
}).validate(data)
|
}).validate(data)
|
||||||
|
|
||||||
|
export const deployValidation = (data: any): Joi.ValidationResult =>
|
||||||
|
Joi.object({
|
||||||
|
appLoc: Joi.string().pattern(/^\//).required().min(2),
|
||||||
|
fileTree: Joi.any().required()
|
||||||
|
}).validate(data)
|
||||||
|
|
||||||
export const fileBodyValidation = (data: any): Joi.ValidationResult =>
|
export const fileBodyValidation = (data: any): Joi.ValidationResult =>
|
||||||
Joi.object({
|
Joi.object({
|
||||||
filePath: Joi.string().pattern(/.sas$/).required().messages({
|
filePath: Joi.string().pattern(/.sas$/).required().messages({
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.31",
|
"version": "0.0.33",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.31",
|
"version": "0.0.33",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"standard-version": "^9.3.2"
|
"standard-version": "^9.3.2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "server",
|
"name": "server",
|
||||||
"version": "0.0.31",
|
"version": "0.0.33",
|
||||||
"description": "NodeJS wrapper for calling the SAS binary executable",
|
"description": "NodeJS wrapper for calling the SAS binary executable",
|
||||||
"repository": "https://github.com/sasjs/server",
|
"repository": "https://github.com/sasjs/server",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
1
web/.nvmrc
Normal file
1
web/.nvmrc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
v16.14.0
|
||||||
17
web/package-lock.json
generated
17
web/package-lock.json
generated
@@ -9215,11 +9215,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prismjs": {
|
"node_modules/prismjs": {
|
||||||
"version": "1.25.0",
|
"version": "1.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
|
||||||
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==",
|
"integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/process": {
|
"node_modules/process": {
|
||||||
"version": "0.11.10",
|
"version": "0.11.10",
|
||||||
@@ -18181,9 +18184,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prismjs": {
|
"prismjs": {
|
||||||
"version": "1.25.0",
|
"version": "1.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz",
|
||||||
"integrity": "sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==",
|
"integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import AppBar from '@mui/material/AppBar'
|
|||||||
import Toolbar from '@mui/material/Toolbar'
|
import Toolbar from '@mui/material/Toolbar'
|
||||||
import Tabs from '@mui/material/Tabs'
|
import Tabs from '@mui/material/Tabs'
|
||||||
import Tab from '@mui/material/Tab'
|
import Tab from '@mui/material/Tab'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
|
||||||
|
|
||||||
|
const NODE_ENV = process.env.NODE_ENV
|
||||||
|
const PORT_API = process.env.PORT_API
|
||||||
|
const baseUrl =
|
||||||
|
NODE_ENV === 'development' ? `http://localhost:${PORT_API ?? 5000}` : ''
|
||||||
|
|
||||||
const Header = (props: any) => {
|
const Header = (props: any) => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
@@ -52,6 +59,18 @@ const Header = (props: any) => {
|
|||||||
component={Link}
|
component={Link}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
<Button
|
||||||
|
href={`${baseUrl}/SASjsApi`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
startIcon={<OpenInNewIcon />}
|
||||||
|
style={{ marginLeft: '50px' }}
|
||||||
|
>
|
||||||
|
API Docs
|
||||||
|
</Button>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const Main = (props: any) => {
|
|||||||
if (props.selectedFilePath) {
|
if (props.selectedFilePath) {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
axios
|
axios
|
||||||
.get(`/SASjsApi/drive/file?filePath=${props.selectedFilePath}`)
|
.get(`/SASjsApi/drive/file?_filePath=${props.selectedFilePath}`)
|
||||||
.then((res: any) => {
|
.then((res: any) => {
|
||||||
setFileContent(res.data)
|
setFileContent(res.data)
|
||||||
})
|
})
|
||||||
@@ -36,6 +36,25 @@ const Main = (props: any) => {
|
|||||||
}
|
}
|
||||||
}, [props.selectedFilePath])
|
}, [props.selectedFilePath])
|
||||||
|
|
||||||
|
const handleDeleteBtnClick = () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
|
||||||
|
const filePath = props.selectedFilePath
|
||||||
|
|
||||||
|
axios
|
||||||
|
.delete(`/SASjsApi/drive/file?_filePath=${filePath}`)
|
||||||
|
.then((res) => {
|
||||||
|
setFileContent('')
|
||||||
|
window.history.pushState('', '', `${baseUrl}/#/SASjsDrive`)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleEditSaveBtnClick = () => {
|
const handleEditSaveBtnClick = () => {
|
||||||
if (!editMode) {
|
if (!editMode) {
|
||||||
setFileContentBeforeEdit(fileContent)
|
setFileContentBeforeEdit(fileContent)
|
||||||
@@ -112,6 +131,13 @@ const Main = (props: any) => {
|
|||||||
direction="row"
|
direction="row"
|
||||||
sx={{ justifyContent: 'center', marginTop: '20px' }}
|
sx={{ justifyContent: 'center', marginTop: '20px' }}
|
||||||
>
|
>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={handleDeleteBtnClick}
|
||||||
|
disabled={isLoading || !props?.selectedFilePath}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleEditSaveBtnClick}
|
onClick={handleEditSaveBtnClick}
|
||||||
|
|||||||
@@ -64,14 +64,16 @@ const SideBar = (props: any) => {
|
|||||||
}, [setFilePathOnMount])
|
}, [setFilePathOnMount])
|
||||||
|
|
||||||
const handleSelect = (node: TreeNode) => {
|
const handleSelect = (node: TreeNode) => {
|
||||||
if (!node.children.length) {
|
if (node.children.length) return
|
||||||
window.history.pushState(
|
|
||||||
'',
|
if (!node.name.includes('.')) return
|
||||||
'',
|
|
||||||
`${baseUrl}/#/SASjsDrive?filePath=${node.relativePath}`
|
window.history.pushState(
|
||||||
)
|
'',
|
||||||
setSelectedFilePath(node.relativePath)
|
'',
|
||||||
}
|
`${baseUrl}/#/SASjsDrive?filePath=${node.relativePath}`
|
||||||
|
)
|
||||||
|
setSelectedFilePath(node.relativePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderTree = (nodes: TreeNode) => (
|
const renderTree = (nodes: TreeNode) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user