Merge pull request 'main' (#349) from main into @hera/dev
Reviewed-on: daoyoucloud/tachycode#349
This commit is contained in:
		
						commit
						1737a0f83b
					
				@ -164,9 +164,9 @@ export class ACL extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    const snippetAllowed = aclRole.snippetAllowed(`${resource}:${action}`);
 | 
			
		||||
 | 
			
		||||
    if (snippetAllowed === false) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    // if (snippetAllowed === false) {
 | 
			
		||||
    //   return null;
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    const fixedParams = this.fixedParamsManager.getParams(resource, action);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -221,11 +221,11 @@ export const ForeignKey = observer(
 | 
			
		||||
    const { getCollection } = useCollectionManager_deprecated();
 | 
			
		||||
    const record = useRecord();
 | 
			
		||||
    const field: any = useField();
 | 
			
		||||
    const { collectionName, target, type, through, name } = record;
 | 
			
		||||
    const { collectionName, target, type, through, name, template } = record;
 | 
			
		||||
    const value = record[field.props.name];
 | 
			
		||||
    const compile = useCompile();
 | 
			
		||||
    const form = useForm();
 | 
			
		||||
    const [initialValue, setInitialValue] = useState(value || field.initialValue);
 | 
			
		||||
    const [initialValue, setInitialValue] = useState(value || (template === 'view' ? null : field.initialValue));
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
      const effectField = ['belongsTo'].includes(type)
 | 
			
		||||
        ? collectionName
 | 
			
		||||
@ -251,9 +251,10 @@ export const ForeignKey = observer(
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }, [type]);
 | 
			
		||||
    const Compoent = template === 'view' ? Select : AutoComplete;
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <AutoComplete
 | 
			
		||||
        <Compoent
 | 
			
		||||
          disabled={disabled}
 | 
			
		||||
          value={initialValue}
 | 
			
		||||
          options={options}
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  protected foreignKeyFields: CollectionFieldOptions[];
 | 
			
		||||
 | 
			
		||||
  getParentCollectionsName() {
 | 
			
		||||
    if (this.parentCollectionsName?.length) {
 | 
			
		||||
      return this.parentCollectionsName;
 | 
			
		||||
    if (this.parentCollectionsName) {
 | 
			
		||||
      return this.parentCollectionsName.slice();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const parents: string[] = [];
 | 
			
		||||
@ -41,8 +41,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getParentCollections() {
 | 
			
		||||
    if (this.parentCollections?.length) {
 | 
			
		||||
      return this.parentCollections;
 | 
			
		||||
    if (this.parentCollections) {
 | 
			
		||||
      return this.parentCollections.slice();
 | 
			
		||||
    }
 | 
			
		||||
    this.parentCollections = this.getParentCollectionsName().map((collectionName) => {
 | 
			
		||||
      return this.collectionManager.getCollection(collectionName);
 | 
			
		||||
@ -52,8 +52,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
 | 
			
		||||
  getChildrenCollectionsName(isSupportView = false) {
 | 
			
		||||
    const cacheKey = isSupportView ? 'supportView' : 'notSupportView';
 | 
			
		||||
    if (this.childrenCollectionsName[cacheKey]?.length) {
 | 
			
		||||
      return this.childrenCollectionsName[cacheKey];
 | 
			
		||||
    if (this.childrenCollectionsName[cacheKey]) {
 | 
			
		||||
      return this.childrenCollectionsName[cacheKey].slice();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const children: string[] = [];
 | 
			
		||||
@ -86,8 +86,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
 | 
			
		||||
  getChildrenCollections(isSupportView = false) {
 | 
			
		||||
    const cacheKey = isSupportView ? 'supportView' : 'notSupportView';
 | 
			
		||||
    if (this.childrenCollections[cacheKey]?.length) {
 | 
			
		||||
      return this.childrenCollections[cacheKey];
 | 
			
		||||
    if (this.childrenCollections[cacheKey]) {
 | 
			
		||||
      return this.childrenCollections[cacheKey].slice();
 | 
			
		||||
    }
 | 
			
		||||
    this.childrenCollections[cacheKey] = this.getChildrenCollectionsName(isSupportView).map((collectionName) => {
 | 
			
		||||
      return this.collectionManager.getCollection(collectionName);
 | 
			
		||||
@ -96,8 +96,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getInheritedFields() {
 | 
			
		||||
    if (this.inheritsFields?.length) {
 | 
			
		||||
      return this.inheritsFields;
 | 
			
		||||
    if (this.inheritsFields) {
 | 
			
		||||
      return this.inheritsFields.slice();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const parentCollections = this.getParentCollectionsName();
 | 
			
		||||
@ -155,8 +155,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAllCollectionsInheritChain() {
 | 
			
		||||
    if (this.allCollectionsInheritChain?.length) {
 | 
			
		||||
      return this.allCollectionsInheritChain;
 | 
			
		||||
    if (this.allCollectionsInheritChain) {
 | 
			
		||||
      return this.allCollectionsInheritChain.slice();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const collectionsInheritChain = [this.name];
 | 
			
		||||
@ -197,8 +197,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getInheritCollectionsChain() {
 | 
			
		||||
    if (this.inheritCollectionsChain?.length) {
 | 
			
		||||
      return this.inheritCollectionsChain;
 | 
			
		||||
    if (this.inheritCollectionsChain) {
 | 
			
		||||
      return this.inheritCollectionsChain.slice();
 | 
			
		||||
    }
 | 
			
		||||
    const collectionsInheritChain = [this.name];
 | 
			
		||||
    const getInheritChain = (name: string) => {
 | 
			
		||||
@ -225,8 +225,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAllFields(predicate?: GetCollectionFieldPredicate) {
 | 
			
		||||
    if (this.allFields?.length) {
 | 
			
		||||
      return this.allFields;
 | 
			
		||||
    if (this.allFields) {
 | 
			
		||||
      return this.allFields.slice();
 | 
			
		||||
    }
 | 
			
		||||
    const currentFields = this.getCurrentFields();
 | 
			
		||||
    const inheritedFields = this.getInheritedFields();
 | 
			
		||||
@ -240,8 +240,8 @@ export class InheritanceCollectionMixin extends Collection {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getForeignKeyFields() {
 | 
			
		||||
    if (this.foreignKeyFields?.length) {
 | 
			
		||||
      return this.foreignKeyFields;
 | 
			
		||||
    if (this.foreignKeyFields) {
 | 
			
		||||
      return this.foreignKeyFields.slice();
 | 
			
		||||
    }
 | 
			
		||||
    const currentFields = this.getCurrentFields();
 | 
			
		||||
    const inheritedFields = this.getInheritedFields();
 | 
			
		||||
 | 
			
		||||
@ -10,10 +10,11 @@ export const CreateChildInitializer = (props) => {
 | 
			
		||||
    'x-component': 'Action',
 | 
			
		||||
    'x-visible': '{{treeTable}}',
 | 
			
		||||
    'x-component-props': {
 | 
			
		||||
      icon: 'PlusOutlined',
 | 
			
		||||
      openMode: 'drawer',
 | 
			
		||||
      type: 'primary',
 | 
			
		||||
      type: 'link',
 | 
			
		||||
      addChild: true,
 | 
			
		||||
      style: { padding: '0px', marginTop: '-5px' },
 | 
			
		||||
      component: 'CreateRecordAction',
 | 
			
		||||
    },
 | 
			
		||||
    properties: {
 | 
			
		||||
      drawer: {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
import { useFieldSchema } from '@formily/react';
 | 
			
		||||
import { useMemo } from 'react';
 | 
			
		||||
import { useSchemaToolbar } from '../../../application';
 | 
			
		||||
import { SchemaSettings } from '../../../application/schema-settings/SchemaSettings';
 | 
			
		||||
import { useCollection_deprecated } from '../../../collection-manager';
 | 
			
		||||
import { useCollection_deprecated, useCollectionManager_deprecated } from '../../../collection-manager';
 | 
			
		||||
import { ButtonEditor } from '../../../schema-component/antd/action/Action.Designer';
 | 
			
		||||
import { SchemaSettingOpenModeSchemaItems } from '../../../schema-items';
 | 
			
		||||
import { SchemaSettingsLinkageRules } from '../../../schema-settings';
 | 
			
		||||
import { SchemaSettingsLinkageRules, SchemaSettingsEnableChildCollections } from '../../../schema-settings';
 | 
			
		||||
 | 
			
		||||
export const addChildActionSettings = new SchemaSettings({
 | 
			
		||||
  name: 'actionSettings:addChild',
 | 
			
		||||
@ -36,6 +38,25 @@ export const addChildActionSettings = new SchemaSettings({
 | 
			
		||||
        openSize: true,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: 'enableChildCollections',
 | 
			
		||||
      Component: SchemaSettingsEnableChildCollections,
 | 
			
		||||
      useVisible() {
 | 
			
		||||
        const { name } = useCollection_deprecated();
 | 
			
		||||
        const { getChildrenCollections } = useCollectionManager_deprecated();
 | 
			
		||||
        const isChildCollectionAction = useMemo(
 | 
			
		||||
          () => getChildrenCollections(name).length > 0,
 | 
			
		||||
          [getChildrenCollections, name],
 | 
			
		||||
        );
 | 
			
		||||
        return isChildCollectionAction;
 | 
			
		||||
      },
 | 
			
		||||
      useComponentProps() {
 | 
			
		||||
        const { name } = useCollection_deprecated();
 | 
			
		||||
        return {
 | 
			
		||||
          collectionName: name,
 | 
			
		||||
        };
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      name: 'delete',
 | 
			
		||||
      type: 'remove',
 | 
			
		||||
 | 
			
		||||
@ -89,11 +89,11 @@ export function ButtonEditor(props) {
 | 
			
		||||
        field.title = title;
 | 
			
		||||
        field.componentProps.icon = icon;
 | 
			
		||||
        field.componentProps.danger = type === 'danger';
 | 
			
		||||
        field.componentProps.type = type;
 | 
			
		||||
        field.componentProps.type = type || field.componentProps.type;
 | 
			
		||||
        fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
 | 
			
		||||
        fieldSchema['x-component-props'].icon = icon;
 | 
			
		||||
        fieldSchema['x-component-props'].danger = type === 'danger';
 | 
			
		||||
        fieldSchema['x-component-props'].type = type;
 | 
			
		||||
        fieldSchema['x-component-props'].type = type || field.componentProps.type;
 | 
			
		||||
        dn.emit('patch', {
 | 
			
		||||
          schema: {
 | 
			
		||||
            ['x-uid']: fieldSchema['x-uid'],
 | 
			
		||||
@ -167,7 +167,7 @@ export function AssignedFieldValues() {
 | 
			
		||||
      <DefaultValueProvider isAllowToSetDefaultValue={() => false}>
 | 
			
		||||
        <SchemaSettingsActionModalItem
 | 
			
		||||
          title={t('Assign field values')}
 | 
			
		||||
          maskClosable={false}
 | 
			
		||||
          // maskClosable={false}
 | 
			
		||||
          initialSchema={initialSchema}
 | 
			
		||||
          initialValues={fieldSchema?.['x-action-settings']?.assignedValues}
 | 
			
		||||
          modalTip={tips[actionType]}
 | 
			
		||||
 | 
			
		||||
@ -292,8 +292,13 @@ function FinallyButton({
 | 
			
		||||
          type={componentType}
 | 
			
		||||
          icon={<DownOutlined />}
 | 
			
		||||
          buttonsRender={([leftButton, rightButton]) => [
 | 
			
		||||
            leftButton,
 | 
			
		||||
            React.cloneElement(rightButton as React.ReactElement<any, string>, { loading: false }),
 | 
			
		||||
            React.cloneElement(leftButton as React.ReactElement<any, string>, {
 | 
			
		||||
              style: props?.style,
 | 
			
		||||
            }),
 | 
			
		||||
            React.cloneElement(rightButton as React.ReactElement<any, string>, {
 | 
			
		||||
              loading: false,
 | 
			
		||||
              style: props?.style,
 | 
			
		||||
            }),
 | 
			
		||||
          ]}
 | 
			
		||||
          menu={menu}
 | 
			
		||||
          onClick={(info) => {
 | 
			
		||||
@ -306,7 +311,13 @@ function FinallyButton({
 | 
			
		||||
      ) : (
 | 
			
		||||
        <Dropdown menu={menu}>
 | 
			
		||||
          {
 | 
			
		||||
            <Button aria-label={props['aria-label']} icon={icon} type={componentType} danger={props.danger}>
 | 
			
		||||
            <Button
 | 
			
		||||
              aria-label={props['aria-label']}
 | 
			
		||||
              icon={icon}
 | 
			
		||||
              type={componentType}
 | 
			
		||||
              danger={props.danger}
 | 
			
		||||
              style={props?.style}
 | 
			
		||||
            >
 | 
			
		||||
              {props.children} <DownOutlined />
 | 
			
		||||
            </Button>
 | 
			
		||||
          }
 | 
			
		||||
@ -337,9 +348,9 @@ function FinallyButton({
 | 
			
		||||
      </Button>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Button
 | 
			
		||||
      {...props}
 | 
			
		||||
      aria-label={props['aria-label']}
 | 
			
		||||
      type={componentType}
 | 
			
		||||
      disabled={field.disabled}
 | 
			
		||||
 | 
			
		||||
@ -90,6 +90,41 @@ describe('string field', () => {
 | 
			
		||||
    console.log(end - begin);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should init sorted value with null scopeValue', async () => {
 | 
			
		||||
    const Test = db.collection({
 | 
			
		||||
      name: 'tests',
 | 
			
		||||
      fields: [
 | 
			
		||||
        {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          name: 'name',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'string',
 | 
			
		||||
          name: 'group',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await db.sync();
 | 
			
		||||
 | 
			
		||||
    await Test.repository.create({
 | 
			
		||||
      values: [
 | 
			
		||||
        {
 | 
			
		||||
          group: null,
 | 
			
		||||
          name: 'r5',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          group: null,
 | 
			
		||||
          name: 'r6',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    Test.setField('sort', { type: 'sort', scopeKey: 'group' });
 | 
			
		||||
 | 
			
		||||
    await db.sync();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should init sorted value with scopeKey', async () => {
 | 
			
		||||
    const Test = db.collection({
 | 
			
		||||
      name: 'tests',
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,22 @@ describe('string operator', () => {
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should escape underscore in inlcude operator', async () => {
 | 
			
		||||
    const u1 = await db.getRepository('users').create({
 | 
			
		||||
      values: {
 | 
			
		||||
        name: 'names of u1',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const u1Res = await db.getRepository('users').findOne({
 | 
			
		||||
      filter: {
 | 
			
		||||
        'name.$includes': '_',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    expect(u1Res).toBeNull();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should query with include operator', async () => {
 | 
			
		||||
    const u1 = await db.getRepository('users').create({
 | 
			
		||||
      values: {
 | 
			
		||||
 | 
			
		||||
@ -99,11 +99,19 @@ export class SortField extends Field {
 | 
			
		||||
 | 
			
		||||
      const whereClause =
 | 
			
		||||
        scopeKey && scopeValue
 | 
			
		||||
          ? `
 | 
			
		||||
  WHERE ${queryInterface.quoteIdentifier(scopeKey)} IN (${scopeValue
 | 
			
		||||
    .filter((v) => v !== null)
 | 
			
		||||
    .map((v) => `'${v}'`)
 | 
			
		||||
    .join(', ')})${scopeValue.includes(null) ? ` OR ${queryInterface.quoteIdentifier(scopeKey)} IS NULL` : ''}`
 | 
			
		||||
          ? (() => {
 | 
			
		||||
              const filteredScopeValue = scopeValue.filter((v) => v !== null);
 | 
			
		||||
              if (filteredScopeValue.length === 0) {
 | 
			
		||||
                return '';
 | 
			
		||||
              }
 | 
			
		||||
              const initialClause = `
 | 
			
		||||
  WHERE ${queryInterface.quoteIdentifier(scopeKey)} IN (${filteredScopeValue.map((v) => `'${v}'`).join(', ')})`;
 | 
			
		||||
 | 
			
		||||
              const nullCheck = scopeValue.includes(null)
 | 
			
		||||
                ? ` OR ${queryInterface.quoteIdentifier(scopeKey)} IS NULL`
 | 
			
		||||
                : '';
 | 
			
		||||
              return initialClause + nullCheck;
 | 
			
		||||
            })()
 | 
			
		||||
          : '';
 | 
			
		||||
 | 
			
		||||
      if (this.collection.db.inDialect('postgres')) {
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,15 @@
 | 
			
		||||
import { Op } from 'sequelize';
 | 
			
		||||
import { isPg } from './utils';
 | 
			
		||||
 | 
			
		||||
function escapeLike(value: string) {
 | 
			
		||||
  return value.replace(/[_%]/g, '\\$&');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  $includes(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `%${item}%`,
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `%${escapeLike(item)}%`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -14,14 +18,14 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `%${value}%`,
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `%${escapeLike(value)}%`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  $notIncludes(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `%${item}%`,
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `%${escapeLike(item)}%`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -30,14 +34,14 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `%${value}%`,
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `%${escapeLike(value)}%`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  $startsWith(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `${item}%`,
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `${escapeLike(item)}%`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -46,14 +50,14 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `${value}%`,
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `${escapeLike(value)}%`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  $notStartsWith(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `${item}%`,
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `${escapeLike(item)}%`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -62,14 +66,14 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `${value}%`,
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `${escapeLike(value)}%`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  $endWith(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `%${item}`,
 | 
			
		||||
        [isPg(ctx) ? Op.iLike : Op.like]: `%${escapeLike(item)}`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -78,14 +82,14 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `%${value}`,
 | 
			
		||||
      [isPg(ctx) ? Op.iLike : Op.like]: `%${escapeLike(value)}`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  $notEndWith(value, ctx) {
 | 
			
		||||
    if (Array.isArray(value)) {
 | 
			
		||||
      const conditions = value.map((item) => ({
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `%${item}`,
 | 
			
		||||
        [isPg(ctx) ? Op.notILike : Op.notLike]: `%${escapeLike(item)}`,
 | 
			
		||||
      }));
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
@ -94,7 +98,7 @@ export default {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `%${value}`,
 | 
			
		||||
      [isPg(ctx) ? Op.notILike : Op.notLike]: `%${escapeLike(value)}`,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
} as Record<string, any>;
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ class ACLPlugin extends Plugin {
 | 
			
		||||
      title: '{{t("Roles & Permissions")}}',
 | 
			
		||||
      icon: 'LockOutlined',
 | 
			
		||||
      Component: RolesManagement,
 | 
			
		||||
      aclSnippet: 'pm.roles',
 | 
			
		||||
      aclSnippet: 'pm.acl.roles',
 | 
			
		||||
      sort: 3,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ describe('snippet', () => {
 | 
			
		||||
    await app.destroy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should not allow to create collections when global allow create', async () => {
 | 
			
		||||
  it.skip('should not allow to create collections when global allow create', async () => {
 | 
			
		||||
    await app.db.getRepository('roles').create({
 | 
			
		||||
      values: {
 | 
			
		||||
        name: 'testRole',
 | 
			
		||||
@ -32,4 +32,26 @@ describe('snippet', () => {
 | 
			
		||||
 | 
			
		||||
    expect(createCollectionResponse.statusCode).toEqual(403);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should allowed when snippet not allowed but resource allowed', async () => {
 | 
			
		||||
    await app.db.getRepository('roles').create({
 | 
			
		||||
      values: {
 | 
			
		||||
        name: 'testRole',
 | 
			
		||||
        strategy: { actions: ['view'] },
 | 
			
		||||
        snippets: ['!ui.*', '!pm', '!pm.*'],
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const testUser = await app.db.getRepository('users').create({
 | 
			
		||||
      values: {
 | 
			
		||||
        roles: ['testRole'],
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const userAgent: any = app.agent().login(testUser);
 | 
			
		||||
 | 
			
		||||
    const listResp = await userAgent.resource('users').list();
 | 
			
		||||
 | 
			
		||||
    expect(listResp.statusCode).toEqual(200);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,22 +1,17 @@
 | 
			
		||||
import { ISchema, useField, useFieldSchema } from '@formily/react';
 | 
			
		||||
import { isValid, uid } from '@formily/shared';
 | 
			
		||||
import { ISchema, useFieldSchema } from '@formily/react';
 | 
			
		||||
import { isValid } from '@formily/shared';
 | 
			
		||||
import {
 | 
			
		||||
  ActionDesigner,
 | 
			
		||||
  DefaultValueProvider,
 | 
			
		||||
  FlagProvider,
 | 
			
		||||
  SchemaSettings,
 | 
			
		||||
  SchemaSettingsActionModalItem,
 | 
			
		||||
  SchemaSettingsItemGroup,
 | 
			
		||||
  SchemaSettingsItemType,
 | 
			
		||||
  SchemaSettingsModalItem,
 | 
			
		||||
  SchemaSettingsSelectItem,
 | 
			
		||||
  AssignedFieldValues,
 | 
			
		||||
  useCompile,
 | 
			
		||||
  useDesignable,
 | 
			
		||||
  useSchemaToolbar,
 | 
			
		||||
} from '@nocobase/client';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import React, { useCallback, useEffect, useState } from 'react';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
 | 
			
		||||
function UpdateMode() {
 | 
			
		||||
  const { dn } = useDesignable();
 | 
			
		||||
@ -135,10 +130,6 @@ const schemaSettingsItems: SchemaSettingsItemType[] = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'assignFieldValues',
 | 
			
		||||
    Component: AssignedFieldValues,
 | 
			
		||||
    // useVisible() {
 | 
			
		||||
    //   const fieldSchema = useFieldSchema();
 | 
			
		||||
    //   return isValid(fieldSchema?.['x-action-settings']?.assignedValues);
 | 
			
		||||
    // },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'afterSuccess',
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,6 @@ export default function addRestoreCommand(app: Application) {
 | 
			
		||||
        groups,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      await app.restart();
 | 
			
		||||
      await app.upgrade();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,6 @@ export class Restorer extends AppMigrator {
 | 
			
		||||
    await this.checkMeta();
 | 
			
		||||
    await this.importCollections(options);
 | 
			
		||||
    await this.importDb(options);
 | 
			
		||||
    await this.upgradeApp();
 | 
			
		||||
    await this.clearWorkDir();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -398,8 +397,4 @@ export class Restorer extends AppMigrator {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async upgradeApp() {
 | 
			
		||||
    await this.app.runCommand('upgrade');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,7 @@ import {
 | 
			
		||||
  useCurrentAppInfo,
 | 
			
		||||
  useAPIClient,
 | 
			
		||||
  useFieldInterfaceOptions,
 | 
			
		||||
  useDataSourceManager,
 | 
			
		||||
} from '@nocobase/client';
 | 
			
		||||
import { ForeignKey } from './components';
 | 
			
		||||
 | 
			
		||||
@ -157,6 +158,7 @@ const useCreateCollectionField = () => {
 | 
			
		||||
  const api = useAPIClient();
 | 
			
		||||
  const record = useRecord();
 | 
			
		||||
  const { name: dataSourceKey } = useParams();
 | 
			
		||||
  const dm = useDataSourceManager();
 | 
			
		||||
  return {
 | 
			
		||||
    async run() {
 | 
			
		||||
      await form.submit();
 | 
			
		||||
@ -176,6 +178,7 @@ const useCreateCollectionField = () => {
 | 
			
		||||
          data: values,
 | 
			
		||||
        });
 | 
			
		||||
        ctx.setVisible(false);
 | 
			
		||||
        dm.getDataSource(dataSourceKey).reload();
 | 
			
		||||
        await form.reset();
 | 
			
		||||
        field.data.loading = false;
 | 
			
		||||
        refresh();
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ const rolesRemoteCollectionsResourcer = {
 | 
			
		||||
      const collectionRepository = new FullDataRepository<any>(dataSource.collectionManager.getCollections());
 | 
			
		||||
 | 
			
		||||
      // all collections
 | 
			
		||||
      const [collections, count] = await collectionRepository.findAndCount();
 | 
			
		||||
      const [collections] = await collectionRepository.findAndCount();
 | 
			
		||||
 | 
			
		||||
      const filterItem = lodash.get(filter, '$and');
 | 
			
		||||
      const filterByTitle = filterItem?.find((item) => item.title);
 | 
			
		||||
@ -45,44 +45,44 @@ const rolesRemoteCollectionsResourcer = {
 | 
			
		||||
        .filter((roleResources) => roleResources.get('usingActionsConfig'))
 | 
			
		||||
        .map((roleResources) => roleResources.get('name'));
 | 
			
		||||
 | 
			
		||||
      const filtedCollections = collections.filter((collection) => {
 | 
			
		||||
        return (
 | 
			
		||||
          (!filterTitle || lodash.get(collection, 'options.title')?.toLowerCase().includes(filterTitle)) &&
 | 
			
		||||
          (!filterName || collection.options.name.toLowerCase().includes(filterName))
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const items = lodash.sortBy(
 | 
			
		||||
        collections
 | 
			
		||||
          .filter((collection) => {
 | 
			
		||||
            return (
 | 
			
		||||
              (!filterTitle || lodash.get(collection, 'options.title')?.toLowerCase().includes(filterTitle)) &&
 | 
			
		||||
              (!filterName || collection.options.name.toLowerCase().includes(filterName))
 | 
			
		||||
            );
 | 
			
		||||
          })
 | 
			
		||||
          .map((collection, i) => {
 | 
			
		||||
            const collectionName = collection.options.name;
 | 
			
		||||
            const exists = roleResourcesNames.includes(collectionName);
 | 
			
		||||
        filtedCollections.map((collection, i) => {
 | 
			
		||||
          const collectionName = collection.options.name;
 | 
			
		||||
          const exists = roleResourcesNames.includes(collectionName);
 | 
			
		||||
 | 
			
		||||
            const usingConfig: UsingConfigType = roleResourceActionResourceNames.includes(collectionName)
 | 
			
		||||
              ? 'resourceAction'
 | 
			
		||||
              : 'strategy';
 | 
			
		||||
          const usingConfig: UsingConfigType = roleResourceActionResourceNames.includes(collectionName)
 | 
			
		||||
            ? 'resourceAction'
 | 
			
		||||
            : 'strategy';
 | 
			
		||||
 | 
			
		||||
            return {
 | 
			
		||||
              type: 'collection',
 | 
			
		||||
              name: collectionName,
 | 
			
		||||
              collectionName,
 | 
			
		||||
              title: collection.options.uiSchema?.title || collection.options.title,
 | 
			
		||||
              roleName: role,
 | 
			
		||||
              usingConfig,
 | 
			
		||||
              exists,
 | 
			
		||||
              fields: [...collection.fields.values()].map((field) => {
 | 
			
		||||
                return field.options;
 | 
			
		||||
              }),
 | 
			
		||||
            };
 | 
			
		||||
          }),
 | 
			
		||||
          return {
 | 
			
		||||
            type: 'collection',
 | 
			
		||||
            name: collectionName,
 | 
			
		||||
            collectionName,
 | 
			
		||||
            title: collection.options.uiSchema?.title || collection.options.title,
 | 
			
		||||
            roleName: role,
 | 
			
		||||
            usingConfig,
 | 
			
		||||
            exists,
 | 
			
		||||
            fields: [...collection.fields.values()].map((field) => {
 | 
			
		||||
              return field.options;
 | 
			
		||||
            }),
 | 
			
		||||
          };
 | 
			
		||||
        }),
 | 
			
		||||
        'name',
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      ctx.body = {
 | 
			
		||||
        count,
 | 
			
		||||
        count: filtedCollections.length,
 | 
			
		||||
        rows: items,
 | 
			
		||||
        page: Number(page),
 | 
			
		||||
        pageSize: Number(pageSize),
 | 
			
		||||
        totalPage: totalPage(count, pageSize),
 | 
			
		||||
        totalPage: totalPage(filtedCollections.length, pageSize),
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      await next();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user