tachybase_todo/packages/core/resourcer/src/utils.ts
katherinehhh af6113c8ef
feat: support for multiple data sources (#3418)
* refactor: collectionName display with tablePrefix

* fix: bug

* fix: schema toolbar no ddata source (T-3182)

* fix: unit test bug

* fix: useAssociationNames support data source

* chore(RecordProvider_deprecated): add collectionName

* fix: deprecated

* refactor: default value

* refactor: default value

* fix: fastRefresh=false

* style: fix action link style (T-3228)

* fix: should not diaplay Save mode for some Action (T-3217)

* chore: remove group title (T-3194)

* fix: extend collections bug

* chore: transaction

* fix: filter block only current data source (T-3226)

* fix: fix filter block in drawer (T-3224)

* fix: avoid error when editing field (T-3232)

* fix: primary key name in postgres

* chore: test

* chore: test

* refactor: forgin key support select and input

* fix: doc bug

* fix: change duplllicte divier name

* feat: throughScope

* fix: bug

* refactor: local improve

* fix: fix parent record of Add child in tree table (T-3235)

* fix: block template filter by dataSource(T-3234)

* chore: change table primary key

* refactor: index for primarykey & unique

* chore: test

* fix: should not display filter blocks option if no association field (T-3242)

* fix: dataSourceKey

* refactor: sourcekey & forginkey & targetkey limit type

* fix: bug

* chore: test

* fix: upload action

* fix: unit test

* fix: useSourceIdFromParentRecord

* fix: permissions

* fix: oho association field should has default fieldnames

* fix: useSourceIdFromParentRecord

* fix: tableSelectorProvider collection undefined

* fix: bug

* chore: validate association keys

* fix: apply mixin bug

* fix: getPrimaryKey

* fix: bug T-3253

* fix:  collection unit test

* chore: validate association keys

* fix: create collection

* fix: getCollection in TableBlockProvider

* refactor: association key in data source manager

* fix: improve doc

* fix(relationshipBlocks): fix sourceId (T-3257,T-3264)

* fix: plugin acl test

* chore: correct field options

* fix: dataScope resource

* fix: improve doc

* fix: appVersion = '<0.20.0-alpha.1'

* refactor: fieldNames

* refactor: primarykey & unique & autoIncrement shuld not support edit in third dataSource

* fix: bug

* fix: gantt block params tree

* fix: style

* fix: wording & icon

* fix: bug

* fix: roles cache

* refactor: calender & express & file collection support preset fields

* fix: decode uri

* refactor: migrate files [wip] (#3584)

* refactor: migrate blockSettings:table

* refactor: migrate fieldSettings:TableColumn

* refactor: migrate TableBlockInitializer

* fix: fix import path

* refactor: migrate TableActionInitailizers

* refactor: migrate TableColumnInitializers

* refactor: migrate TableActionColumnInitializers

* refactor: migrate TableColumnSchemaToolbar

* refactor: migrate TableSelectorInitializer

* refactor: migrate blockSettings:tableSelector

* refactor(tableSelector): migrate e2e

* refactor(form): migrate e2e

* refactor: migrate FormBlockInitializer

* refactor: migrate CreateFormBlockInitializer

* refactor: migrate RecordFormBlockInitializer

* refactor: migrate blockSettings:createForm

* refactor: rename file name

* refactor: migrate blockSettings:editForm

* refactor: migrate FormActionInitailizers

* refactor: move to a new file

* refactor: migrate formItemInitializers

* refactor: migrate FormItemSchemaToolbar

* refactor: migrate fieldSettings:FormItem

* chore: fix build

* fix: fix weird path error

* fix: rename formActionInitializers

* fix: create collection field

* refactor: throughCollection

* fix: datasources get permission

* fix: throughCollection

* fix: throughCollection

* fix: register initializer components

* refactor: targetkey & source key must be unique

* refactor: targetkey & source key must be unique index

* fix(customRequest): avoid error when clicking button

* chore: error message when add multiple primary keys

* fix: target key in hasMany

* fix: default value should not support edit in outside dataSource

* fix: test

* fix: update associations (#3586)

* fix: source key

* fix: addAccessor

* fix: updateAssociations

* fix: bugs

* fix: remove test.only

* refactor: migrate RecordReadPrettyFormBlockInitializer

* refactor: migrate singleDataDetailsBlockSettings

* fix(users): filter bug

* refactor: migrate readPrettyFormActionInitializers

* refactor: migrate readPrettyFormItemInitializers

* refactor: migrate DetailsBlockInitializer

* refactor: migrate multiDataDetailsBlockSettings

* feat: validate association key pairs

* chore: default title

* refactor: migrate detailsActionInitializers

* refactor: migrate e2e

* refactor: migrate ListBlockInitializer

* refactor: migrate listBlockSettings

* refactor: migrate listActionInitializers

* refactor: migrate listItemActionInitializers

* fix: create collection

* fix: remove fieldsHistoryRepository.createMany

* test(e2e): fix error message for roles.name

* fix: sync indexes in postgres

* chore: test

* test: acl test

* test(e2e): fix sort error

* refactor: remove useless code

* test: kanban e2e

* fix: load user

* fix: test

* test: fix unit tests

* fix: db.sync

* test: updateRole

* fix: test

* fix: settings and initializer performance improve

* fix: update role resources

* fix: add block

* fix: fix T-3308

* test: fix e2e

* test(e2e): skip fix block

* chore: skip test in sqlite

* fix: change initializer menu key

* test(collectionManager): fix e2e

* refactor: sort field availableTypes

* fix: client core performance optimization

* refactor(GridCard): migrate e2e

* refactor: migrate GridCard

* fix: bug

* refactor: migrate utils

* refactor: migrate filter-form

* fix: change Record to CollectionRecord

* chore: acl migration

* chore: acl migration

* chore: migration of acl

* refactor: migrate Collapse

* chore: error message

* fix: update associations

* chore: update collection search to be case-insensitive

* refactor: migrate Markdown

* fix(WorkflowTodos): x-toolbar typo

* feat: admin change password

* feat: check foreign key && target key value in update associations

* chore: dataSource permission

* refactor: dataSource permission

* fix: acl support data source permission

* fix: fix T-3307

* chore: test

* refactor: locale improve

* chore: locale

* chore: sqlite test config

* chore: create user with roles test

* chore: test

* test: fix mock data to avoid duplication

* chore: test

* fix: load table with tablePrefix

* chore: move action in datasource

* chore: number field to sort field type

* test: optimize dropdown

* chore: upgrade @playwright/test to v1.42.1

* fix: fix invalid path for Windows

* test: fix e2e

* chore: kanban Sort field

* fix: kanban

* fix: kanban

* refactor: create sort in kanban

* refactor: create sort field in kanban

* refactor: locale improve

* refactor: locale improve

* fix: sync with null default value

* refactor: collectionFieldInterfaceSelect

* fix: move action

* fix: update associations

* fix: test case

* chore: test

* test: optimize e2e

* feat: remvoe Duplicate for single details block (T-3195)

* fix(fieldNames): should use primaryKey as default value (T-3322, T-3319)

* fix: use filterTargetKey as fieldNNames.value

* test: fix e2e

* test: fix e2e

* test(kanban): fix e2e

* test(blockTemplate): should clear template at end of test

* refactor: migrate fields

* refactor: migrate actions

* refactor: migrate menu

* refactor: migrate page

* refactor(SchemaSettings): unify naming style

* fix: scopeKeyOptions undefined

* refactor(SchemaInitializers): unify naming stle

* fix(bi): chart filter fields

* chore: acl snippets

* refactor: replace CreateFormBlockInitializers to blockInitializers:createForm

* refactor: replace to blockInitializers:customizeCreateForm

* refactor: replace block intializers name

* refactor: replace action initializers name

* refactor: replace field initializers name

* style: fix hover style for column action (T-3297)

* refactor: revert some codes

* chore: update comment

* fix: revert record deprected

* fix: remove pro-plugins

* fix: bug

* chore: replace iframeBlockSchemaSettings to blockSettings:iframe

* Revert "refactor: revert some codes"

This reverts commit 991021ceaeecc5d27113a51e501a4abd439edcd2.

* Revert "refactor: replace field initializers name"

This reverts commit b47b808d06305741b56302e3dad1dd256658fad4.

* Revert "refactor: replace action initializers name"

This reverts commit eab1b6e3d986d1c3dc80d75fa6230fa948e3a33e.

* Revert "refactor: replace block intializers name"

This reverts commit 50ab9da177f344d037184a17746cb1d0e037a826.

* Revert "refactor: replace to blockInitializers:customizeCreateForm"

This reverts commit 77b9f59bb14d944fd8c42006e899861196589748.

* Revert "refactor: replace CreateFormBlockInitializers to blockInitializers:createForm"

This reverts commit e9a38b0b4d9fabc571b7d9cdc8929914f5e2a367.

* Revert "refactor(SchemaInitializers): unify naming stle"

This reverts commit 542390899fa84d212a8dbbe7f77e0f19befa6ae8.

* Revert "refactor(SchemaSettings): unify naming style"

This reverts commit 8566735922c4a157efccdb3830deaedeb08c6f6a.

* Revert "chore: replace iframeBlockSchemaSettings to blockSettings:iframe"

This reverts commit 884f6df92fdc860a50500025f132904e9528002f.

* refactor: create sorting field in kanban

* refactor: create sorting field in kanban

* fix: style

* fix: bug

* fix(SideMenu): fix the problem of invalid add menu (T-3331)

* fix: translation

* feat: client en-US docs

---------

Co-authored-by: xilesun <2013xile@gmail.com>
Co-authored-by: dream2023 <1098626505@qq.com>
Co-authored-by: Zeke Zhang <958414905@qq.com>
Co-authored-by: chenos <chenlinxh@gmail.com>
Co-authored-by: Chareice <chareice@live.com>
2024-03-03 23:06:24 +08:00

288 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import _ from 'lodash';
// @ts-ignore
import { pathToRegexp } from 'path-to-regexp';
import qs from 'qs';
import { ResourceType } from './resource';
export interface ParseRequest {
path: string;
method: string;
namespace?: string;
// 资源类型
type?: ResourceType;
}
export interface ParseOptions {
prefix?: string;
accessors?: {
list?: string;
create?: string;
get?: string;
update?: string;
delete?: string;
set?: string;
add?: string;
};
}
export interface ParsedParams {
actionName?: string;
resourceName?: string;
resourceIndex?: string;
associatedName?: string;
associatedIndex?: string;
// table: string;
// tableKey?: string | number;
// relatedTable?: string;
// relatedKey?: string | number;
// action?: ActionName,
}
export function getNameByParams(params: ParsedParams): string {
const { resourceName, associatedName } = params;
return associatedName ? `${associatedName}.${resourceName}` : resourceName;
}
export function parseRequest(request: ParseRequest, options: ParseOptions = {}): ParsedParams | false {
const accessors = {
// 常规 actions
list: 'list',
create: 'create',
get: 'get',
update: 'update',
delete: 'destroy',
// associate 操作
add: 'add',
set: 'set',
remove: 'remove',
...(options.accessors || {}),
};
const keys = [];
const regexp = pathToRegexp('/resourcer/{:associatedName.}?:resourceName{\\::actionName}', keys);
const reqPath = decodeURI(request.path);
const matches = regexp.exec(reqPath);
if (matches) {
const params = {};
keys.forEach((obj, index) => {
if (matches[index + 1] === undefined) {
return;
}
params[obj.name] = matches[index + 1];
});
return params;
}
const defaults = {
single: {
'/:resourceName': {
get: accessors.list,
post: accessors.create,
delete: accessors.delete,
},
'/:resourceName/:resourceIndex': {
get: accessors.get,
put: accessors.update,
patch: accessors.update,
delete: accessors.delete,
},
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.list,
post: accessors.create,
delete: accessors.delete,
},
'/:associatedName/:associatedIndex/:resourceName/:resourceIndex': {
get: accessors.get,
post: accessors.create,
put: accessors.update,
patch: accessors.update,
delete: accessors.delete,
},
},
hasOne: {
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.get,
post: accessors.update,
put: accessors.update,
patch: accessors.update,
delete: accessors.delete,
},
},
hasMany: {
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.list,
post: accessors.create,
delete: accessors.delete,
},
'/:associatedName/:associatedIndex/:resourceName/:resourceIndex': {
get: accessors.get,
post: accessors.create,
put: accessors.update,
patch: accessors.update,
delete: accessors.delete,
},
},
belongsTo: {
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.get,
delete: accessors.remove,
},
'/:associatedName/:associatedIndex/:resourceName/:resourceIndex': {
post: accessors.set,
},
},
belongsToMany: {
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.list,
post: accessors.set,
},
'/:associatedName/:associatedIndex/:resourceName/:resourceIndex': {
get: accessors.get,
post: accessors.add,
put: accessors.update, // Many to Many 的 update 是针对 through
patch: accessors.update, // Many to Many 的 update 是针对 through
delete: accessors.remove,
},
},
set: {
'/:associatedName/:associatedIndex/:resourceName': {
get: accessors.list,
post: accessors.add,
delete: accessors.remove,
},
},
};
const params: ParsedParams = {};
let prefix = (options.prefix || '').trim().replace(/\/$/, '');
if (prefix && !prefix.startsWith('/')) {
prefix = `/${prefix}`;
}
const { type = 'single' } = request;
for (const path in defaults[type]) {
const keys = [];
const regexp = pathToRegexp(`${prefix}${path}`, keys, {});
const matches = regexp.exec(reqPath);
if (!matches) {
continue;
}
keys.forEach((obj, index) => {
if (matches[index + 1] === undefined) {
return;
}
params[obj.name] = matches[index + 1];
});
params.actionName = _.get(defaults, [type, path, request.method.toLowerCase()]);
}
if (Object.keys(params).length === 0) {
return false;
}
if (params.resourceName) {
const [resourceName, actionName] = params.resourceName.split(':');
if (actionName) {
params.resourceName = resourceName;
params.actionName = actionName;
}
}
return params;
}
export function parseQuery(input: string): any {
// 自带 query 处理的不太给力,需要用 qs 转一下
const query = qs.parse(input, {
// 原始 query string 中如果一个键连等号“=”都没有可以被认为是 null 类型
strictNullHandling: true,
// 逗号分隔转换为数组
// comma: true,
});
// filter 支持 json string
if (typeof query.filter === 'string') {
query.filter = JSON.parse(query.filter);
}
return query;
}
export function parseFields(fields: any) {
if (!fields) {
return {};
}
if (typeof fields === 'string') {
fields = fields.split(',').map((field) => field.trim());
}
if (Array.isArray(fields)) {
const onlyFields = [];
const output: any = {};
fields.forEach((item) => {
if (typeof item === 'string') {
onlyFields.push(item);
} else if (typeof item === 'object') {
if (item.only) {
onlyFields.push(...item.only.toString().split(','));
}
Object.assign(output, parseFields(item));
}
});
if (onlyFields.length) {
output.only = onlyFields;
}
return output;
}
if (fields.only && typeof fields.only === 'string') {
fields.only = fields.only.split(',').map((field) => field.trim());
}
if (fields.except && typeof fields.except === 'string') {
fields.except = fields.except.split(',').map((field) => field.trim());
}
if (fields.appends && typeof fields.appends === 'string') {
fields.appends = fields.appends.split(',').map((field) => field.trim());
}
return fields;
}
export function mergeFields(defaults: any, inputs: any) {
let fields: any = {};
defaults = parseFields(defaults);
inputs = parseFields(inputs);
if (inputs.only) {
// 前端提供 only后端提供 only
if (defaults.only) {
fields.only = defaults.only.filter((field) => inputs.only.includes(field));
}
// 前端提供 only后端提供 except输出 only 排除 except
else if (defaults.except) {
fields.only = inputs.only.filter((field) => !defaults.except.includes(field));
}
// 前端提供 only后端没有提供 only 或 except
else {
fields.only = inputs.only;
}
} else if (inputs.except) {
// 前端提供 except后端提供 only只输出 only 里排除 except 的字段
if (defaults.only) {
fields.only = defaults.only.filter((field) => !inputs.except.includes(field));
}
// 前端提供 except后端提供 except 或不提供,合并 except
else {
fields.except = _.uniq([...inputs.except, ...(defaults.except || [])]);
}
}
// 前端没提供 only 或 except
else {
fields = defaults;
}
// 如果前端提供了 appends
if (!_.isEmpty(inputs.appends)) {
fields.appends = _.uniq([...inputs.appends, ...(defaults.appends || [])]);
}
if (!fields.appends) {
fields.appends = [];
}
return fields;
}