Skip to content

Commit

Permalink
refactor: rework functions implementation (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
clement-berard authored Dec 6, 2024
1 parent ffe8e28 commit 541ad2e
Show file tree
Hide file tree
Showing 14 changed files with 1,632 additions and 205 deletions.
2 changes: 1 addition & 1 deletion .node-red-dxprc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"builder": {
"esbuildControllerOptions": {
"includeInBundle": ["webdav"]
"includeInBundle": ["webdav", "radash"]
}
}
}
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"enabled": true,
"rules": {
"recommended": true,
"complexity": { "noForEach": "off" },
"suspicious": {
"noExplicitAny": "off"
}
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@keload/node-red-dxp": "^1.14.0",
"@release-it/conventional-changelog": "9.0.3",
"@types/jquery": "3.5.32",
"@types/jqueryui": "1.12.23",
"@types/node": "^22.9.0",
"@types/node": "^22.10.1",
"@types/node-red": "1.3.5",
"radash": "^12.1.0",
"release-it": "17.10.0",
"tsx": "4.19.2",
"typescript": "^5.6.3"
},
"dependencies": {
"@keload/node-red-dxp": "^1.8.0",
"superstruct": "2.0.2",
"webdav": "^5.7.1"
},
Expand Down
1,457 changes: 1,316 additions & 141 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions src/common/constants-client-side.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { title } from 'radash';

export const NODES_CATEGORY = 'webdav';
export const NODES_COLOR = '#00A6FB';
export const NODES_ICONS = 'font-awesome/fa-folder-open';

type WebdavMethods = {
methodName: string;
label?: string;
enabled?: boolean;
};

export const webdavMethods: WebdavMethods[] = [
{
methodName: 'copyFile',
},
{
methodName: 'createDirectory',
},
{
methodName: 'createReadStream',
},
{
methodName: 'createWriteStream',
},
{
methodName: 'customRequest',
},
{
methodName: 'deleteFile',
label: 'Delete (Folder/File)',
enabled: true,
},
{
methodName: 'exists',
},
{
methodName: 'getDirectoryContents',
enabled: true,
},
{
methodName: 'getFileContents',
},
{
methodName: 'getFileDownloadLink',
},
{
methodName: 'getFileUploadLink',
},
{
methodName: 'getQuota',
},
{
methodName: 'getStat',
},
{
methodName: 'lock',
},
{
methodName: 'moveFile',
},
{
methodName: 'putFileContents',
},
{
methodName: 'unlock',
},
] as const;

export const cleanWebdavMethods = webdavMethods
.map((method) => {
return {
...method,
label: method?.label || title(method.methodName),
};
})
.filter((method) => method?.enabled);

export const cleanWebdavMethodsForSelect = cleanWebdavMethods.map((method) => ({
value: method.methodName,
text: method.label,
}));

export const methodsEnum = webdavMethods.filter((s) => s?.enabled).map((method) => method.methodName);
3 changes: 0 additions & 3 deletions src/common/constants.ts

This file was deleted.

33 changes: 33 additions & 0 deletions src/common/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as sstruct from 'superstruct';
import type { Infer } from 'superstruct';
import { methodsEnum } from './constants-client-side';

export const methodsSchema = sstruct.enums(methodsEnum);

export const actionValidators = {
getDirectoryContents: {
schema: sstruct.object({
details: sstruct.optional(sstruct.boolean()),
deep: sstruct.optional(sstruct.boolean()),
directory: sstruct.string(),
}),
},
deleteFile: {
schema: sstruct.object({
directory: sstruct.string(),
}),
},
};

export function validateAction(actionName: string, toValidate: Record<string, any>) {
const [errValidate] = sstruct
.assign(
sstruct.object({
action: methodsSchema,
}),
actionValidators?.[actionName]?.schema || {},
)
.validate({ action: actionName, ...toValidate });

return errValidate;
}
18 changes: 13 additions & 5 deletions src/common/webdavClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createClient } from 'webdav';
import { AuthType, createClient } from 'webdav';

type GetWebDavClientParams = {
host: string;
Expand All @@ -8,18 +8,26 @@ type GetWebDavClientParams = {
};

export function getWebDavClient(params: GetWebDavClientParams) {
const basePath = params.basePath || '/';
// let basePath = params.basePath || '';
//
// if (basePath === '/') {
// basePath = '';
// }
//
// if (basePath !== '' && basePath.endsWith('/')) {
// basePath = basePath.slice(0, -1);
// }

return createClient(`${params.host}${basePath}`, {
return createClient(`${params.host}`, {
username: params?.user,
password: params?.password,
remoteBasePath: basePath,
remoteBasePath: '/',
authType: AuthType.Auto,
});
}

export function resolveWebDavClient(serverConfigId: string) {
const webdavServer = RED.nodes.getNode(serverConfigId);

// @ts-ignore
return getWebDavClient({
// @ts-ignore
Expand Down
55 changes: 46 additions & 9 deletions src/nodes/list/controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import type { NodeControllerConfig, NodeControllerInst } from '@keload/node-red-dxp/editor';
import { tryit } from '@keload/node-red-dxp/utils';
import { omit, tryit } from 'radash';
import { methodsSchema, validateAction } from '../../common/validator';
import { resolveWebDavClient } from '../../common/webdavClient';
import type { NodeListProps } from './types';

function resolveActionData(config: NodeControllerConfig<any>, action: string, innerPayload: Record<any, any>) {
const realData = omit(innerPayload || config[action], ['action']);
const errValidate = validateAction(action, realData);

return [errValidate, realData] as const;
}

export default function (this: NodeControllerInst<NodeListProps>, config: NodeControllerConfig<NodeListProps>) {
RED.nodes.createNode(this, config);

Expand All @@ -11,30 +19,59 @@ export default function (this: NodeControllerInst<NodeListProps>, config: NodeCo
const action = config.action;

const actions = {
list: () => {
console.log('config.listDirectory', config.listDirectory);
return tryit(webDavClient.getDirectoryContents)(config.listDirectory || '/');
getDirectoryContents: (innerPayload: Record<any, any>) => {
const [err, resp] = resolveActionData(config, 'getDirectoryContents', innerPayload);
if (err) {
return [err] as const;
}

const { directory, ...options } = resp || {};
return tryit(webDavClient.getDirectoryContents)(directory || '/', options || {});
},
delete: () => {
return tryit(webDavClient.deleteFile)(config.deleteDirectory);
deleteFile: (innerPayload: Record<any, any>) => {
const [err, resp] = resolveActionData(config, 'deleteFile', innerPayload);
if (err) {
return [err] as const;
}

const { directory, ...options } = resp || {};
return tryit(webDavClient.deleteFile)(directory || '/');
},
};

this.on('input', async (msg) => {
// @ts-ignore
const [, innerPayload] = tryit(RED.util.evaluateNodeProperty)(config.entry, config.entryType, this, msg) as [
Error,
any,
];

const currentAction = innerPayload?.action || action;

const [errValidationAction] = methodsSchema.validate(currentAction);

if (errValidationAction) {
this.error({
message: errValidationAction.message,
});
return;
}

const handler = actions[action];
if (handler) {
const [err, resp] = await handler();
const [err, resp] = await handler(innerPayload);
const errResponse = err?.response as Response;
if (err) {
this.error({
message: err.message,
status: errResponse.status,
statusText: errResponse.statusText,
status: errResponse?.status,
statusText: errResponse?.statusText,
});
return;
}

this.send({
...msg,
payload: resp,
});
} else {
Expand Down
File renamed without changes.
95 changes: 74 additions & 21 deletions src/nodes/list/editor/index.html
Original file line number Diff line number Diff line change
@@ -1,28 +1,81 @@
<div class="form-row">
<label for="node-input-webdavServer"><i class="fa fa-tv"></i> Server</label>
<input type="text" id="node-input-webdavServer"/>
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-tv"></i>
<label for="node-input-webdavServer">Server</label>
</div>
<input type="text" id="node-input-webdavServer"/>
</div>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="custom name"/>
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-tag"></i>
<label for="node-input-name">Name</label>
</div>
<input type="text" id="node-input-name"/>
</div>
</div>
<div class="form-row">
<label for="node-input-action" ><i class="fa fa-target"></i> Action</label>
<select id="node-input-action">
<option value=""></option>
<option value="list">List</option>
<option value="delete">Delete</option>
</select>
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-ellipsis-h"></i>
<label for="node-input-entry">Property</label>
</div>
<input type="text" id="node-input-entry">
<input type="hidden" id="node-input-entryType">
</div>
<div class="hint">
If empty, configuration below will be used.
</div>
</div>
<div class="action-list hidden">
<div class="form-row">
<label for="node-input-listDirectory"><i class="fa fa-sitemap"></i> Directory</label>
<input type="text" id="node-input-listDirectory" />
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-bolt"></i>
<label for="node-input-action">Action</label>
</div>
<select id="node-input-action"></select>
</div>
</div>
<div class="extra-params getDirectoryContents">
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-folder"></i>
<label for="node-input-getDirectoryContents-directory">Directory</label>
</div>
<input type="text" id="node-input-getDirectoryContents-directory"/>
</div>
</div>
<!-- <div class="dxp-form-row">-->
<!-- <div class="main">-->
<!-- <div>-->
<!-- <label for="node-input-getDirectoryContents-deep">Deep</label>-->
<!-- </div>-->
<!-- <input type="checkbox" id="node-input-getDirectoryContents-deep"/>-->
<!-- </div>-->
<!-- </div>-->
<div class="dxp-form-row">
<div class="main">
<div>
<label for="node-input-getDirectoryContents-details">Details</label>
</div>
<input type="checkbox" id="node-input-getDirectoryContents-details"/>
</div>
</div>
</div>
<div class="action-delete hidden">
<div class="form-row">
<label for="node-input-deleteDirectory"><i class="fa fa-sitemap"></i> Directory</label>
<input type="text" id="node-input-deleteDirectory" />
<div class="extra-params deleteFile">
<div class="dxp-form-row">
<div class="main">
<div>
<i class="fa fa-folder"></i>
<label for="node-input-deleteFile-directory">Directory</label>
</div>
<input type="text" id="node-input-deleteFile-directory"/>
</div>
<div class="hint">
Folder/File. If folder, it must end with a slash.
</div>
</div>
</div>
Loading

0 comments on commit 541ad2e

Please sign in to comment.