Browse Source

refactor: nocoExecute (#9456)

pull/9458/head
Pranav C 3 months ago committed by GitHub
parent
commit
78c1762784
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      packages/nocodb/src/controllers/old-datas/old-datas.service.ts
  2. 2
      packages/nocodb/src/controllers/public-datas-export.controller.ts
  3. 2
      packages/nocodb/src/db/BaseModelSqlv2.ts
  4. 2
      packages/nocodb/src/helpers/dataHelpers.ts
  5. 2
      packages/nocodb/src/services/data-table.service.ts
  6. 2
      packages/nocodb/src/services/datas.service.ts
  7. 2
      packages/nocodb/src/services/public-datas.service.ts
  8. 1
      packages/nocodb/src/utils/index.ts
  9. 205
      packages/nocodb/src/utils/nocoExecute.ts

2
packages/nocodb/src/controllers/old-datas/old-datas.service.ts

@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import type { OldPathParams } from '~/helpers/dataHelpers';
import type { NcContext } from '~/interface/config';
import getAst from '~/helpers/getAst';

2
packages/nocodb/src/controllers/public-datas-export.controller.ts

@ -8,7 +8,7 @@ import {
} from '@nestjs/common';
import { isSystemColumn, ViewTypes } from 'nocodb-sdk';
import * as XLSX from 'xlsx';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import papaparse from 'papaparse';
import { NcError } from '~/helpers/catchError';
import getAst from '~/helpers/getAst';

2
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -5,7 +5,7 @@ import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import timezone from 'dayjs/plugin/timezone';
import equal from 'fast-deep-equal';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import {
AuditOperationSubTypes,
AuditOperationTypes,

2
packages/nocodb/src/helpers/dataHelpers.ts

@ -1,4 +1,4 @@
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import { isSystemColumn, UITypes } from 'nocodb-sdk';
import * as XLSX from 'xlsx';
import papaparse from 'papaparse';

2
packages/nocodb/src/services/data-table.service.ts

@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { isLinksOrLTAR, RelationTypes, ViewTypes } from 'nocodb-sdk';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import { validatePayload } from 'src/helpers';
import type { LinkToAnotherRecordColumn } from '~/models';
import type { NcContext } from '~/interface/config';

2
packages/nocodb/src/services/datas.service.ts

@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { isSystemColumn } from 'nocodb-sdk';
import * as XLSX from 'xlsx';
import papaparse from 'papaparse';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
import type { PathParams } from '~/helpers/dataHelpers';
import type { NcContext } from '~/interface/config';

2
packages/nocodb/src/services/public-datas.service.ts

@ -1,6 +1,6 @@
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { UITypes, ViewTypes } from 'nocodb-sdk';
import { nocoExecute } from 'nc-help';
import { nocoExecute } from '~/utils';
import type { LinkToAnotherRecordColumn } from '~/models';
import type { NcContext } from '~/interface/config';
import { Column, Model, Source, View } from '~/models';

1
packages/nocodb/src/utils/index.ts

@ -2,5 +2,6 @@ export * from './dataUtils';
export * from './sanitiseUserObj';
export * from './emailUtils';
export * from './circularReplacer';
export * from './nocoExecute';
export const isEE = false;

205
packages/nocodb/src/utils/nocoExecute.ts

@ -0,0 +1,205 @@
// Define the interface for the request object with possible nested structure
import { Logger } from '@nestjs/common';
interface XcRequest {
[key: string]: XcRequest | 1 | true;
}
const logger = new Logger('nocoExecute');
// Helper function to flatten a nested array recursively
const flattenArray = (res) => {
return Array.isArray(res) ? res.flatMap((r) => flattenArray(r)) : res;
};
/**
* Execute the request object
* @param requestObj request object
* @param resolverObj resolver object which may contain resolver functions or data
* @param dataTree data tree that holds the state of the resolved data
* @param rootArgs root arguments passed for nested processing
* @returns Promise<any> returns the resolved data
**/
const nocoExecute = async (
requestObj: XcRequest,
resolverObj,
dataTree = {},
rootArgs = null,
): Promise<any> => {
// Handle array of resolvers by executing nocoExecute on each and returning a Promise.all
if (Array.isArray(resolverObj)) {
return Promise.all(
resolverObj.map((resolver, i) =>
nocoExecute(
requestObj,
resolver,
(dataTree[i] = dataTree[i] || {}),
rootArgs,
),
),
);
}
const res = {};
/**
* Recursively extract nested data from the dataTree and resolve it.
* @param path the path of keys to traverse in the data tree
* @param dataTreeObj the current data tree object
* @param resolver the resolver object to call functions or return values
* @param args arguments passed to resolver functions
* @returns Promise resolving the nested value
*/
const extractNested = (
path,
dataTreeObj: any,
resolver = {},
args = {},
): any => {
if (path.length) {
const key = path[0];
// If key doesn't exist in dataTree, resolve using resolver or create a placeholder
if (dataTreeObj[key] === undefined || dataTreeObj[key] === null) {
if (typeof resolver[key] === 'function') {
dataTreeObj[path[0]] = resolver[key](args); // Call resolver function
} else if (typeof resolver[key] === 'object') {
dataTreeObj[path[0]] = Promise.resolve(resolver[key]); // Resolve object directly
} else if (dataTreeObj?.__proto__?.__columnAliases?.[path[0]]) {
// Handle column alias lookup
dataTreeObj[path[0]] = extractNested(
dataTreeObj?.__proto__?.__columnAliases?.[path[0]]?.path,
dataTreeObj,
{},
args,
);
} else {
if (typeof dataTreeObj === 'object') {
dataTreeObj[path[0]] = Promise.resolve(resolver[key]);
}
}
} else if (typeof dataTreeObj[key] === 'function') {
// If the key is a function, invoke it with args
dataTreeObj.__proto__ = {
...dataTreeObj.__proto__,
[key]: dataTreeObj[key](args),
};
}
// Recursively handle nested arrays or resolve promises
return (
dataTreeObj[path[0]] instanceof Promise
? dataTreeObj[path[0]]
: Promise.resolve(dataTreeObj[path[0]])
).then((res1) => {
if (Array.isArray(res1)) {
return Promise.all(
res1.map((r) => extractNested(path.slice(1), r, {}, args)),
);
} else {
return res1 !== null && res1 !== undefined
? extractNested(path.slice(1), res1, {}, args)
: Promise.resolve(null);
}
});
} else {
return Promise.resolve(dataTreeObj); // If path is exhausted, return data tree object
}
};
/**
* Extract the value for the given key from the resolver object or data tree.
* If the key is a function, call it with args, otherwise resolve the value.
* @param key the key to extract
* @param args the arguments for nested extractions
*/
function extractField(key, args) {
// Check if the key is a column alias or needs to be resolved
if (!resolverObj?.__proto__?.__columnAliases?.[key]) {
if (resolverObj) {
// Resolve if it's a function, object, or value
if (typeof resolverObj[key] === 'function') {
res[key] = resolverObj[key](args); // Call function
} else if (typeof resolverObj[key] === 'object') {
res[key] = Promise.resolve(resolverObj[key]); // Resolve object
} else {
try {
res[key] = Promise.resolve(resolverObj[key]); // Resolve value
} catch (e) {
logger.error(e);
}
}
}
dataTree[key] = res[key]; // Store result in dataTree
} else {
// If nested, extract the nested value using extractNested function
res[key] = extractNested(
resolverObj?.__proto__?.__columnAliases?.[key]?.path,
dataTree,
resolverObj,
args?.nested?.[key],
).then((res1) => {
return Promise.resolve(
// Flatten the array if it's nested
Array.isArray(res1) ? flattenArray(res1) : res1,
);
});
}
}
// Determine which keys to extract from the request object or resolver object
const extractKeys =
requestObj && typeof requestObj === 'object'
? Object.keys(requestObj).filter((k) => requestObj[k])
: Object.keys(resolverObj);
const out: any = {}; // Holds the final output
const resolPromises = []; // Holds all the promises for asynchronous resolution
for (const key of extractKeys) {
// Extract the field for each key
extractField(key, rootArgs?.nested?.[key]);
// Handle nested request objects by recursively calling nocoExecute
if (requestObj[key] && typeof requestObj[key] === 'object') {
res[key] = res[key].then((res1) => {
if (Array.isArray(res1)) {
// Handle arrays of results by executing nocoExecute on each element
return (dataTree[key] = Promise.all(
res1.map((r, i) =>
nocoExecute(
requestObj[key] as XcRequest,
r,
dataTree?.[key]?.[i],
rootArgs?.nested?.[key],
),
),
));
} else if (res1) {
// Handle single objects
return (dataTree[key] = nocoExecute(
requestObj[key] as XcRequest,
res1,
dataTree[key],
rootArgs?.nested?.[key],
));
}
return res1; // Return result if no further nesting
});
}
// Push resolved promises to resolPromises array
if (res[key]) {
resolPromises.push(
(async () => {
out[key] = await res[key];
})(),
);
}
}
// Wait for all promises to resolve before returning the final output
await Promise.all(resolPromises);
return out; // Return the final resolved output
};
export { nocoExecute };
Loading…
Cancel
Save