openxiangda-contracts 2.92.0 → 2.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2510,6 +2510,7 @@ const ANONYMOUS_PUBLIC_OPERATIONS = new Set([
2510
2510
  'own.read',
2511
2511
  'public.list',
2512
2512
  'public.read',
2513
+ 'public.query',
2513
2514
  ]);
2514
2515
  function validateAnonymousPublicAccess(value, config, pointer) {
2515
2516
  const access = object(value, pointer);
@@ -2535,6 +2536,7 @@ function validateAnonymousPublicAccess(value, config, pointer) {
2535
2536
  'serverGeneratedFields',
2536
2537
  'schedule',
2537
2538
  'publicSubtableFields',
2539
+ 'publicQueries',
2538
2540
  'draft',
2539
2541
  'validations',
2540
2542
  ]);
@@ -2707,19 +2709,148 @@ function validateAnonymousPublicAccess(value, config, pointer) {
2707
2709
  }
2708
2710
  }
2709
2711
  }
2712
+ const publicRecordFields = Object.prototype.hasOwnProperty.call(policy, 'publicRecordFields')
2713
+ ? uniqueStrings(policy.publicRecordFields, policyPointer + '/publicRecordFields', 64)
2714
+ : [];
2710
2715
  if (operations.some(operation => operation.startsWith('public.'))) {
2711
2716
  if (!Object.prototype.hasOwnProperty.call(policy, 'publicRecordFields')) {
2712
- fail('NATIVE_PUBLIC_RECORD_FIELDS_REQUIRED', `${policyPointer}/publicRecordFields`);
2717
+ fail('NATIVE_PUBLIC_RECORD_FIELDS_REQUIRED', policyPointer + '/publicRecordFields');
2713
2718
  }
2714
- const publicRecordFields = uniqueStrings(policy.publicRecordFields, `${policyPointer}/publicRecordFields`, 64);
2715
2719
  if (publicRecordFields.length < 1 ||
2716
2720
  publicRecordFields.some(field => !fields.includes(field)) ||
2717
2721
  publicRecordFields.some(field => declaredFields.get(field) === 'signature' ||
2718
2722
  (declaredFields.get(field) === 'subtable' &&
2719
2723
  !publicSubtableFields?.[field]))) {
2720
- fail('NATIVE_PUBLIC_RECORD_FIELDS_INVALID', `${policyPointer}/publicRecordFields`);
2724
+ fail('NATIVE_PUBLIC_RECORD_FIELDS_INVALID', policyPointer + '/publicRecordFields');
2721
2725
  }
2722
2726
  }
2727
+ const publicQueries = Object.prototype.hasOwnProperty.call(policy, 'publicQueries')
2728
+ ? boundedArray(policy.publicQueries, policyPointer + '/publicQueries', 16)
2729
+ : [];
2730
+ const queryCodes = new Set();
2731
+ publicQueries.forEach((rawQuery, queryIndex) => {
2732
+ const queryPointer = policyPointer + '/publicQueries/' + queryIndex;
2733
+ const query = object(rawQuery, queryPointer);
2734
+ exactKeys(query, ['code', 'kind', 'resourceCode', 'fields', 'defaultLimit', 'maxLimit'], queryPointer, ['sort', 'parameters', 'aggregate']);
2735
+ const queryCode = stableCode(query.code, queryPointer + '/code');
2736
+ if (queryCodes.has(queryCode)) {
2737
+ fail('NATIVE_PUBLIC_QUERY_DUPLICATE', queryPointer + '/code');
2738
+ }
2739
+ queryCodes.add(queryCode);
2740
+ if (!['records', 'aggregate'].includes(String(query.kind))) {
2741
+ fail('NATIVE_PUBLIC_QUERY_KIND_INVALID', queryPointer + '/kind');
2742
+ }
2743
+ if (String(query.resourceCode) !== declaredResourceCode) {
2744
+ fail('NATIVE_PUBLIC_QUERY_RESOURCE_INVALID', queryPointer + '/resourceCode');
2745
+ }
2746
+ const queryFields = uniqueStrings(query.fields, queryPointer + '/fields', 64);
2747
+ if (queryFields.length < 1 ||
2748
+ queryFields.some(field => !fields.includes(field)) ||
2749
+ queryFields.some(field => !publicRecordFields?.includes(field))) {
2750
+ fail('NATIVE_PUBLIC_QUERY_FIELDS_INVALID', queryPointer + '/fields');
2751
+ }
2752
+ const defaultLimit = Number(query.defaultLimit);
2753
+ const maxLimit = Number(query.maxLimit);
2754
+ if (!Number.isSafeInteger(defaultLimit) ||
2755
+ !Number.isSafeInteger(maxLimit) ||
2756
+ defaultLimit < 1 ||
2757
+ maxLimit < defaultLimit ||
2758
+ maxLimit > 100) {
2759
+ fail('NATIVE_PUBLIC_QUERY_LIMIT_INVALID', queryPointer + '/maxLimit');
2760
+ }
2761
+ if (Object.prototype.hasOwnProperty.call(query, 'sort')) {
2762
+ const sort = boundedArray(query.sort, queryPointer + '/sort', 4);
2763
+ const sortFields = new Set();
2764
+ sort.forEach((rawSort, sortIndex) => {
2765
+ const sortPointer = queryPointer + '/sort/' + sortIndex;
2766
+ const item = object(rawSort, sortPointer);
2767
+ exactKeys(item, ['field', 'direction'], sortPointer);
2768
+ const field = fieldCodeValue(item.field, sortPointer + '/field');
2769
+ if (!queryFields.includes(field) || sortFields.has(field) || !['asc', 'desc'].includes(String(item.direction))) {
2770
+ fail('NATIVE_PUBLIC_QUERY_SORT_INVALID', sortPointer);
2771
+ }
2772
+ sortFields.add(field);
2773
+ });
2774
+ }
2775
+ if (Object.prototype.hasOwnProperty.call(query, 'parameters')) {
2776
+ const parameters = boundedArray(query.parameters, queryPointer + '/parameters', 8);
2777
+ const names = new Set();
2778
+ parameters.forEach((rawParameter, parameterIndex) => {
2779
+ const parameterPointer = queryPointer + '/parameters/' + parameterIndex;
2780
+ const parameter = object(rawParameter, parameterPointer);
2781
+ exactKeys(parameter, ['name', 'field', 'operator', 'type'], parameterPointer);
2782
+ const name = stableCode(parameter.name, parameterPointer + '/name');
2783
+ const field = fieldCodeValue(parameter.field, parameterPointer + '/field');
2784
+ if (names.has(name) ||
2785
+ !queryFields.includes(field) ||
2786
+ !['eq', 'contains', 'in'].includes(String(parameter.operator)) ||
2787
+ !['string', 'number', 'boolean', 'string[]'].includes(String(parameter.type))) {
2788
+ fail('NATIVE_PUBLIC_QUERY_PARAMETER_INVALID', parameterPointer);
2789
+ }
2790
+ names.add(name);
2791
+ });
2792
+ }
2793
+ if (Object.prototype.hasOwnProperty.call(query, 'aggregate')) {
2794
+ if (String(query.kind) !== 'aggregate') {
2795
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_KIND_INVALID', queryPointer + '/aggregate');
2796
+ }
2797
+ const aggregatePointer = queryPointer + '/aggregate';
2798
+ const aggregate = object(query.aggregate, aggregatePointer);
2799
+ exactKeys(aggregate, ['measures'], aggregatePointer, ['dimensions', 'order']);
2800
+ const aliases = new Set();
2801
+ const aggregateAlias = (value, pointer) => {
2802
+ const normalized = String(value || '').trim();
2803
+ if (!/^[A-Za-z][A-Za-z0-9_]{0,62}$/.test(normalized) || aliases.has(normalized)) {
2804
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_ALIAS_INVALID', pointer);
2805
+ }
2806
+ aliases.add(normalized);
2807
+ return normalized;
2808
+ };
2809
+ const dimensions = Object.prototype.hasOwnProperty.call(aggregate, 'dimensions')
2810
+ ? boundedArray(aggregate.dimensions, aggregatePointer + '/dimensions', 3)
2811
+ : [];
2812
+ dimensions.forEach((rawDimension, dimensionIndex) => {
2813
+ const pointer = `${aggregatePointer}/dimensions/${dimensionIndex}`;
2814
+ const dimension = object(rawDimension, pointer);
2815
+ exactKeys(dimension, ['field', 'as'], pointer, ['bucket']);
2816
+ const field = fieldCodeValue(dimension.field, pointer + '/field');
2817
+ if (!queryFields.includes(field))
2818
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_FIELD_INVALID', pointer + '/field');
2819
+ aggregateAlias(dimension.as, pointer + '/as');
2820
+ if (dimension.bucket !== undefined && !['day', 'week', 'month', 'quarter', 'year'].includes(String(dimension.bucket))) {
2821
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_BUCKET_INVALID', pointer + '/bucket');
2822
+ }
2823
+ });
2824
+ const measures = boundedArray(aggregate.measures, aggregatePointer + '/measures', 8);
2825
+ if (measures.length < 1)
2826
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_MEASURES_INVALID', aggregatePointer + '/measures');
2827
+ measures.forEach((rawMeasure, measureIndex) => {
2828
+ const pointer = `${aggregatePointer}/measures/${measureIndex}`;
2829
+ const measure = object(rawMeasure, pointer);
2830
+ exactKeys(measure, ['type', 'as'], pointer, ['field']);
2831
+ const type = String(measure.type);
2832
+ const field = measure.field === undefined ? undefined : fieldCodeValue(measure.field, pointer + '/field');
2833
+ if (!['count', 'countDistinct', 'sum', 'avg', 'min', 'max'].includes(type) ||
2834
+ (type === 'count' ? field !== undefined : !field || !queryFields.includes(field))) {
2835
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_MEASURE_INVALID', pointer);
2836
+ }
2837
+ aggregateAlias(measure.as, pointer + '/as');
2838
+ });
2839
+ if (Object.prototype.hasOwnProperty.call(aggregate, 'order')) {
2840
+ const order = boundedArray(aggregate.order, aggregatePointer + '/order', 10);
2841
+ order.forEach((rawOrder, orderIndex) => {
2842
+ const pointer = `${aggregatePointer}/order/${orderIndex}`;
2843
+ const item = object(rawOrder, pointer);
2844
+ exactKeys(item, ['field'], pointer, ['direction', 'nulls']);
2845
+ if (!aliases.has(String(item.field)) ||
2846
+ (item.direction !== undefined && !['asc', 'desc'].includes(String(item.direction))) ||
2847
+ (item.nulls !== undefined && !['first', 'last'].includes(String(item.nulls)))) {
2848
+ fail('NATIVE_PUBLIC_QUERY_AGGREGATE_ORDER_INVALID', pointer);
2849
+ }
2850
+ });
2851
+ }
2852
+ }
2853
+ });
2723
2854
  const requiredFields = Object.prototype.hasOwnProperty.call(policy, 'requiredFields')
2724
2855
  ? uniqueStrings(policy.requiredFields, `${policyPointer}/requiredFields`, 64)
2725
2856
  : [];
@@ -2818,6 +2949,75 @@ function compileAnonymousPublicAccess(config) {
2818
2949
  .sort((left, right) => compareText(left.field, right.field)),
2819
2950
  }
2820
2951
  : {}),
2952
+ ...(policy.publicQueries
2953
+ ? {
2954
+ publicQueries: sorted(policy.publicQueries.map((query) => ({
2955
+ code: query.code,
2956
+ kind: query.kind,
2957
+ resourceCode: query.resourceCode,
2958
+ fields: uniqueSorted(query.fields),
2959
+ defaultLimit: Number(query.defaultLimit),
2960
+ maxLimit: Number(query.maxLimit),
2961
+ ...(query.sort
2962
+ ? {
2963
+ sort: query.sort
2964
+ .map((item) => ({
2965
+ field: item.field,
2966
+ direction: item.direction,
2967
+ }))
2968
+ .sort((left, right) => compareText(left.field, right.field)),
2969
+ }
2970
+ : {}),
2971
+ ...(query.parameters
2972
+ ? {
2973
+ parameters: query.parameters
2974
+ .map((parameter) => ({
2975
+ name: parameter.name,
2976
+ field: parameter.field,
2977
+ operator: parameter.operator,
2978
+ type: parameter.type,
2979
+ }))
2980
+ .sort((left, right) => compareText(left.name, right.name)),
2981
+ }
2982
+ : {}),
2983
+ ...(query.aggregate
2984
+ ? {
2985
+ aggregate: {
2986
+ ...(query.aggregate.dimensions
2987
+ ? {
2988
+ dimensions: query.aggregate.dimensions
2989
+ .map((dimension) => ({
2990
+ field: dimension.field,
2991
+ as: dimension.as,
2992
+ ...(dimension.bucket ? { bucket: dimension.bucket } : {}),
2993
+ }))
2994
+ .sort((left, right) => left.as.localeCompare(right.as)),
2995
+ }
2996
+ : {}),
2997
+ measures: query.aggregate.measures
2998
+ .map((measure) => ({
2999
+ type: measure.type,
3000
+ ...(measure.field ? { field: measure.field } : {}),
3001
+ as: measure.as,
3002
+ }))
3003
+ .sort((left, right) => left.as.localeCompare(right.as)),
3004
+ ...(query.aggregate.order
3005
+ ? {
3006
+ order: query.aggregate.order
3007
+ .map((item) => ({
3008
+ field: item.field,
3009
+ ...(item.direction ? { direction: item.direction } : {}),
3010
+ ...(item.nulls ? { nulls: item.nulls } : {}),
3011
+ }))
3012
+ .sort((left, right) => left.field.localeCompare(right.field)),
3013
+ }
3014
+ : {}),
3015
+ },
3016
+ }
3017
+ : {}),
3018
+ })), (query) => String(query.code)),
3019
+ }
3020
+ : {}),
2821
3021
  ...(policy.publicSubtableFields
2822
3022
  ? {
2823
3023
  publicSubtableFields: Object.fromEntries(Object.entries(policy.publicSubtableFields)