feat: add $isCurrentUser filter operator (#299)
* feat: add $isCurrentUser filter operator * fix: supports three levels
This commit is contained in:
parent
a37609e71b
commit
f426c8a3ba
@ -12,6 +12,7 @@ export async function get(ctx: Context, next) {
|
||||
appends,
|
||||
except,
|
||||
filter,
|
||||
context: ctx,
|
||||
});
|
||||
|
||||
ctx.body = instance;
|
||||
|
@ -33,6 +33,7 @@ async function listWithPagination(ctx: Context) {
|
||||
const repository = getRepositoryFromParams(ctx);
|
||||
|
||||
const [rows, count] = await repository.findAndCount({
|
||||
context: ctx,
|
||||
...findArgs(ctx.action.params),
|
||||
...pageArgsToLimitArgs(parseInt(String(page)), parseInt(String(pageSize))),
|
||||
});
|
||||
|
@ -33,6 +33,16 @@ export const createdBy: IField = {
|
||||
},
|
||||
filterable: {
|
||||
children: [
|
||||
{
|
||||
name: 'id',
|
||||
title: '{{t("ID")}}',
|
||||
operators: operators.id,
|
||||
schema: {
|
||||
title: '{{t("ID")}}',
|
||||
type: 'number',
|
||||
'x-component': 'InputNumber',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nickname',
|
||||
title: '{{t("Nickname")}}',
|
||||
|
@ -70,8 +70,16 @@ export const number = [
|
||||
export const id = [
|
||||
{ label: '{{t("is")}}', value: '$eq', selected: true },
|
||||
{ label: '{{t("is not")}}', value: '$ne' },
|
||||
{ label: '{{t("Exists")}}', value: '$exists', noValue: true },
|
||||
{ label: '{{t("Not exists")}}', value: '$notExists', noValue: true },
|
||||
{
|
||||
label: '{{t("is current logged-in user")}}',
|
||||
value: '$isCurrentUser',
|
||||
noValue: true,
|
||||
visible(field) {
|
||||
return field.collectionName === 'users';
|
||||
},
|
||||
},
|
||||
{ label: '{{t("exists")}}', value: '$exists', noValue: true },
|
||||
{ label: '{{t("not exists")}}', value: '$notExists', noValue: true },
|
||||
];
|
||||
|
||||
export const enumType = [
|
||||
|
@ -32,6 +32,16 @@ export const updatedBy: IField = {
|
||||
},
|
||||
filterable: {
|
||||
children: [
|
||||
{
|
||||
name: 'id',
|
||||
title: '{{t("ID")}}',
|
||||
operators: operators.id,
|
||||
schema: {
|
||||
title: '{{t("ID")}}',
|
||||
type: 'number',
|
||||
'x-component': 'InputNumber',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nickname',
|
||||
title: '{{t("Nickname")}}',
|
||||
|
@ -10,8 +10,9 @@ export const useFilterOptions = (collectionName: string) => {
|
||||
const nonfilterable = fieldSchema?.['x-component-props']?.nonfilterable || [];
|
||||
const { getCollectionFields, getInterface } = useCollectionManager();
|
||||
const fields = getCollectionFields(collectionName);
|
||||
const field2option = (field, nochildren) => {
|
||||
if (nonfilterable.length && !nochildren && nonfilterable.includes(field.name)) {
|
||||
let depth = 0;
|
||||
const field2option = (field) => {
|
||||
if (nonfilterable.length && depth !== 1 && nonfilterable.includes(field.name)) {
|
||||
return;
|
||||
}
|
||||
if (!field.interface) {
|
||||
@ -26,9 +27,15 @@ export const useFilterOptions = (collectionName: string) => {
|
||||
name: field.name,
|
||||
title: field?.uiSchema?.title || field.name,
|
||||
schema: field?.uiSchema,
|
||||
operators: operators || [],
|
||||
operators:
|
||||
operators?.filter?.((operator) => {
|
||||
return !operator?.visible || operator.visible(field);
|
||||
}) || [],
|
||||
};
|
||||
if (nochildren) {
|
||||
if (field.target && depth > 2) {
|
||||
return;
|
||||
}
|
||||
if (depth > 2) {
|
||||
return option;
|
||||
}
|
||||
if (children?.length) {
|
||||
@ -36,16 +43,17 @@ export const useFilterOptions = (collectionName: string) => {
|
||||
}
|
||||
if (nested) {
|
||||
const targetFields = getCollectionFields(field.target);
|
||||
const options = getOptions(targetFields, true);
|
||||
const options = getOptions(targetFields).filter(Boolean);
|
||||
option['children'] = option['children'] || [];
|
||||
option['children'].push(...options);
|
||||
}
|
||||
return option;
|
||||
};
|
||||
const getOptions = (fields, nochildren = false) => {
|
||||
const getOptions = (fields) => {
|
||||
++depth;
|
||||
const options = [];
|
||||
fields.forEach((field) => {
|
||||
const option = field2option(field, nochildren);
|
||||
const option = field2option(field);
|
||||
if (option) {
|
||||
options.push(option);
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ export const useValues = () => {
|
||||
const operator = field.data?.operators?.find?.((item) => item.value === operatorValue);
|
||||
field.data.operator = operator;
|
||||
field.data.schema = merge(field.data.schema, operator.schema);
|
||||
field.data.value = operator.noValue ? true : null;
|
||||
field.data.value = operator.noValue ? operator.default || true : null;
|
||||
data2value();
|
||||
console.log('setOperator', field.data);
|
||||
},
|
||||
|
@ -1,14 +1,13 @@
|
||||
import { compile } from '@formily/json-schema/lib/compiler';
|
||||
import { SchemaExpressionScopeContext, SchemaOptionsContext } from '@formily/react';
|
||||
import { Schema, SchemaExpressionScopeContext, SchemaOptionsContext } from '@formily/react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const useCompile = () => {
|
||||
const options = useContext(SchemaOptionsContext);
|
||||
const scope = useContext(SchemaExpressionScopeContext);
|
||||
return (source: any) => {
|
||||
return (source: any, ext?: any) => {
|
||||
if (!source) {
|
||||
return source;
|
||||
}
|
||||
return compile(source, { ...options.scope, ...scope });
|
||||
return Schema.compile(source, { ...options.scope, ...scope, ...ext });
|
||||
};
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { applyMixins, AsyncEmitter } from '@nocobase/utils';
|
||||
import merge from 'deepmerge';
|
||||
import { EventEmitter } from 'events';
|
||||
import lodash from 'lodash';
|
||||
import { ModelCtor, Op, Options, QueryInterfaceDropAllTablesOptions, Sequelize, SyncOptions, Utils } from 'sequelize';
|
||||
import { Collection, CollectionOptions, RepositoryType } from './collection';
|
||||
import { ImporterReader, ImportFileExtension } from './collection-importer';
|
||||
@ -11,7 +12,6 @@ import { ModelHook } from './model-hook';
|
||||
import extendOperators from './operators';
|
||||
import { RelationRepository } from './relation-repository/relation-repository';
|
||||
import { Repository } from './repository';
|
||||
import lodash from 'lodash';
|
||||
|
||||
export interface MergeOptions extends merge.Options {}
|
||||
|
||||
@ -34,6 +34,7 @@ interface RegisterOperatorsContext {
|
||||
db?: Database;
|
||||
path?: string;
|
||||
field?: Field;
|
||||
app?: any;
|
||||
}
|
||||
|
||||
export interface CleanOptions extends QueryInterfaceDropAllTablesOptions {
|
||||
|
@ -11,6 +11,7 @@ type FilterType = any;
|
||||
|
||||
interface FilterParserContext {
|
||||
collection: Collection;
|
||||
app?: any;
|
||||
}
|
||||
|
||||
export default class FilterParser {
|
||||
@ -125,6 +126,7 @@ export default class FilterParser {
|
||||
const queryValue = lodash.get(unflatten(originalFiler), skipPrefix);
|
||||
|
||||
value = opKey(queryValue, {
|
||||
app: this.context.app,
|
||||
db: this.database,
|
||||
path: skipPrefix,
|
||||
fieldName: this.getFieldNameFromQueryPath(skipPrefix),
|
||||
|
@ -1,4 +1,4 @@
|
||||
export { ModelCtor, SyncOptions } from 'sequelize';
|
||||
export { ModelCtor, Op, SyncOptions } from 'sequelize';
|
||||
export * from './collection';
|
||||
export * from './database';
|
||||
export { Database as default } from './database';
|
||||
|
@ -26,7 +26,12 @@ export class OptionsParser {
|
||||
this.model = collection.model;
|
||||
this.options = options;
|
||||
this.database = collection.context.database;
|
||||
this.filterParser = new FilterParser(options?.filter, { collection });
|
||||
this.filterParser = new FilterParser(options?.filter, {
|
||||
collection,
|
||||
app: {
|
||||
ctx: options?.context,
|
||||
},
|
||||
});
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ export class HasManyRepository extends MultipleRelationRepository implements IHa
|
||||
];
|
||||
|
||||
if (options && options['filter']) {
|
||||
const filterResult = this.parseFilter(options['filter']);
|
||||
const filterResult = this.parseFilter(options['filter'], options);
|
||||
|
||||
if (filterResult.include && filterResult.include.length > 0) {
|
||||
return await this.destroyByFilter(options['filter'], transaction);
|
||||
|
@ -1,8 +1,5 @@
|
||||
import { RelationRepository, transaction } from './relation-repository';
|
||||
import lodash, { omit } from 'lodash';
|
||||
import { omit } from 'lodash';
|
||||
import { MultiAssociationAccessors, Op, Sequelize, Transaction } from 'sequelize';
|
||||
import { UpdateGuard } from '../update-guard';
|
||||
import { updateModelByValues } from '../update-associations';
|
||||
import {
|
||||
CommonFindOptions,
|
||||
CountOptions,
|
||||
@ -10,11 +7,14 @@ import {
|
||||
Filter,
|
||||
FilterByTk,
|
||||
FindOptions,
|
||||
TK,
|
||||
TargetKey,
|
||||
TK,
|
||||
TransactionAble,
|
||||
UpdateOptions,
|
||||
UpdateOptions
|
||||
} from '../repository';
|
||||
import { updateModelByValues } from '../update-associations';
|
||||
import { UpdateGuard } from '../update-guard';
|
||||
import { RelationRepository, transaction } from './relation-repository';
|
||||
|
||||
export interface FindAndCountOptions extends CommonFindOptions {}
|
||||
|
||||
@ -187,10 +187,11 @@ export abstract class MultipleRelationRepository extends RelationRepository {
|
||||
});
|
||||
}
|
||||
|
||||
protected filterHasInclude(filter: Filter) {
|
||||
const filterResult = this.parseFilter(filter);
|
||||
protected filterHasInclude(filter: Filter, options?: any) {
|
||||
const filterResult = this.parseFilter(filter, options);
|
||||
return filterResult.include && filterResult.include.length > 0;
|
||||
}
|
||||
|
||||
protected accessors() {
|
||||
return <MultiAssociationAccessors>super.accessors();
|
||||
}
|
||||
|
@ -90,9 +90,12 @@ export abstract class RelationRepository {
|
||||
return { ...options, ...params };
|
||||
}
|
||||
|
||||
protected parseFilter(filter: Filter) {
|
||||
protected parseFilter(filter: Filter, options?: any) {
|
||||
const parser = new FilterParser(filter, {
|
||||
collection: this.targetCollection,
|
||||
app: {
|
||||
ctx: options?.context,
|
||||
},
|
||||
});
|
||||
return parser.toSequelizeParams();
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
ModelCtor,
|
||||
Op,
|
||||
Transaction,
|
||||
UpdateOptions as SequelizeUpdateOptions,
|
||||
UpdateOptions as SequelizeUpdateOptions
|
||||
} from 'sequelize';
|
||||
import { Collection } from './collection';
|
||||
import { Database } from './database';
|
||||
@ -74,6 +74,7 @@ export interface CommonFindOptions {
|
||||
appends?: Appends;
|
||||
except?: Except;
|
||||
sort?: Sort;
|
||||
context?: any;
|
||||
}
|
||||
|
||||
interface FindOneOptions extends FindOptions, CommonFindOptions {}
|
||||
@ -187,7 +188,7 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
||||
if (countOptions?.filter) {
|
||||
options = {
|
||||
...options,
|
||||
...this.parseFilter(countOptions.filter),
|
||||
...this.parseFilter(countOptions.filter, countOptions),
|
||||
};
|
||||
}
|
||||
|
||||
@ -453,9 +454,12 @@ export class Repository<TModelAttributes extends {} = any, TCreationAttributes e
|
||||
return { where: {}, ...options, ...params };
|
||||
}
|
||||
|
||||
protected parseFilter(filter: Filter) {
|
||||
protected parseFilter(filter: Filter, options?: any) {
|
||||
const parser = new FilterParser(filter, {
|
||||
collection: this.collection,
|
||||
app: {
|
||||
ctx: options?.context,
|
||||
},
|
||||
});
|
||||
return parser.toSequelizeParams();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Collection } from '@nocobase/database';
|
||||
import { Collection, Op } from '@nocobase/database';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import { resolve } from 'path';
|
||||
import * as actions from './actions/users';
|
||||
@ -25,6 +25,13 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
|
||||
}
|
||||
|
||||
async beforeLoad() {
|
||||
this.db.registerOperators({
|
||||
$isCurrentUser(_, ctx) {
|
||||
return {
|
||||
[Op.eq]: ctx?.app?.ctx?.state?.currentUser?.id || -1,
|
||||
};
|
||||
},
|
||||
});
|
||||
this.db.registerModels({ UserModel });
|
||||
this.db.on('users.afterCreateWithAssociations', async (model, options) => {
|
||||
const { transaction } = options;
|
||||
|
Loading…
Reference in New Issue
Block a user