openxiangda-contracts 2.11.0 → 2.12.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.
@@ -1632,6 +1632,8 @@ const ANONYMOUS_PUBLIC_OPERATIONS = new Set([
1632
1632
  'create',
1633
1633
  'own.list',
1634
1634
  'own.read',
1635
+ 'public.list',
1636
+ 'public.read',
1635
1637
  ]);
1636
1638
  function validateAnonymousPublicAccess(value, config, pointer) {
1637
1639
  const access = object(value, pointer);
@@ -1645,19 +1647,26 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1645
1647
  resource,
1646
1648
  ]));
1647
1649
  const policyCodes = new Set();
1648
- const routeCodes = new Set();
1650
+ const declaredPolicyCodes = new Set(policies.map((rawPolicy) => String(rawPolicy?.code || '')));
1649
1651
  policies.forEach((rawPolicy, index) => {
1650
1652
  const policyPointer = `${pointer}/policies/${index}`;
1651
1653
  const policy = object(rawPolicy, policyPointer);
1652
- exactKeys(policy, ['code', 'routeCode', 'mode', 'resourceCode', 'operations', 'fields'], policyPointer, ['requiredFields', 'ownRecordFields', 'draft', 'validations']);
1654
+ exactKeys(policy, ['code', 'routeCode', 'mode', 'resourceCode', 'operations', 'fields'], policyPointer, [
1655
+ 'requiredFields',
1656
+ 'ownRecordFields',
1657
+ 'publicRecordFields',
1658
+ 'publicFilters',
1659
+ 'serverGeneratedFields',
1660
+ 'schedule',
1661
+ 'publicSubtableFields',
1662
+ 'draft',
1663
+ 'validations',
1664
+ ]);
1653
1665
  const code = stableCode(policy.code, `${policyPointer}/code`);
1654
1666
  if (policyCodes.has(code)) {
1655
1667
  fail('NATIVE_PUBLIC_POLICY_DUPLICATE', `${policyPointer}/code`);
1656
1668
  }
1657
1669
  const routeCode = stableCode(policy.routeCode, `${policyPointer}/routeCode`);
1658
- if (routeCodes.has(routeCode)) {
1659
- fail('NATIVE_PUBLIC_ROUTE_DUPLICATE', `${policyPointer}/routeCode`);
1660
- }
1661
1670
  const route = routes.get(routeCode);
1662
1671
  if (!route ||
1663
1672
  route.surface !== 'user' ||
@@ -1676,7 +1685,7 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1676
1685
  String(field.code),
1677
1686
  String(field.type),
1678
1687
  ]));
1679
- const operations = uniqueStrings(policy.operations, `${policyPointer}/operations`, 6);
1688
+ const operations = uniqueStrings(policy.operations, `${policyPointer}/operations`, 8);
1680
1689
  if (operations.length < 1 ||
1681
1690
  operations.some(operation => !ANONYMOUS_PUBLIC_OPERATIONS.has(operation))) {
1682
1691
  fail('NATIVE_PUBLIC_OPERATION_INVALID', `${policyPointer}/operations`);
@@ -1688,7 +1697,11 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1688
1697
  if (fields.length < 1 || fields.some(field => !declaredFields.has(field))) {
1689
1698
  fail('NATIVE_PUBLIC_FIELD_INVALID', `${policyPointer}/fields`);
1690
1699
  }
1691
- for (const optionalFieldSet of ['requiredFields', 'ownRecordFields']) {
1700
+ for (const optionalFieldSet of [
1701
+ 'requiredFields',
1702
+ 'ownRecordFields',
1703
+ 'publicRecordFields',
1704
+ ]) {
1692
1705
  if (!Object.prototype.hasOwnProperty.call(policy, optionalFieldSet))
1693
1706
  continue;
1694
1707
  const selected = uniqueStrings(policy[optionalFieldSet], `${policyPointer}/${optionalFieldSet}`, 64);
@@ -1696,6 +1709,137 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1696
1709
  fail('NATIVE_PUBLIC_FIELD_SCOPE_INVALID', `${policyPointer}/${optionalFieldSet}`);
1697
1710
  }
1698
1711
  }
1712
+ const serverGeneratedFields = Object.prototype.hasOwnProperty.call(policy, 'serverGeneratedFields')
1713
+ ? boundedArray(policy.serverGeneratedFields, `${policyPointer}/serverGeneratedFields`, 16)
1714
+ : [];
1715
+ const generatedFieldCodes = new Set();
1716
+ serverGeneratedFields.forEach((rawGenerated, generatedIndex) => {
1717
+ const generatedPointer = `${policyPointer}/serverGeneratedFields/${generatedIndex}`;
1718
+ const generated = object(rawGenerated, generatedPointer);
1719
+ exactKeys(generated, ['field', 'kind'], generatedPointer);
1720
+ const field = fieldCodeValue(generated.field, `${generatedPointer}/field`);
1721
+ if (generatedFieldCodes.has(field) ||
1722
+ !declaredFields.has(field) ||
1723
+ fields.includes(field) ||
1724
+ (declaredFields.get(field) !== 'text.short' &&
1725
+ declaredFields.get(field) !== 'text.long')) {
1726
+ fail('NATIVE_PUBLIC_SERVER_GENERATED_FIELD_INVALID', `${generatedPointer}/field`);
1727
+ }
1728
+ equal(generated.kind, 'random-token', `${generatedPointer}/kind`);
1729
+ generatedFieldCodes.add(field);
1730
+ });
1731
+ const schedule = Object.prototype.hasOwnProperty.call(policy, 'schedule')
1732
+ ? object(policy.schedule, `${policyPointer}/schedule`)
1733
+ : null;
1734
+ if (schedule) {
1735
+ const scheduleKeys = [
1736
+ 'campusPolicyCode',
1737
+ 'rulePolicyCode',
1738
+ 'campusField',
1739
+ 'ruleCampusField',
1740
+ 'ruleWeekdaysField',
1741
+ 'ruleOpenAtField',
1742
+ 'ruleCloseAtField',
1743
+ 'ruleSlotMinutesField',
1744
+ 'ruleAdvanceHoursField',
1745
+ 'ruleAdvanceDaysField',
1746
+ 'campusEnabledField',
1747
+ 'ruleEnabledField',
1748
+ 'dateField',
1749
+ 'timeField',
1750
+ ];
1751
+ exactKeys(schedule, scheduleKeys, `${policyPointer}/schedule`);
1752
+ for (const key of ['campusPolicyCode', 'rulePolicyCode']) {
1753
+ stableCode(schedule[key], `${policyPointer}/schedule/${key}`);
1754
+ }
1755
+ for (const key of scheduleKeys.filter(key => !['campusPolicyCode', 'rulePolicyCode'].includes(key))) {
1756
+ fieldCodeValue(schedule[key], `${policyPointer}/schedule/${key}`);
1757
+ }
1758
+ for (const field of [schedule.campusField, schedule.dateField, schedule.timeField]) {
1759
+ if (!fields.includes(String(field))) {
1760
+ fail('NATIVE_PUBLIC_SCHEDULE_FIELD_INVALID', `${policyPointer}/schedule`);
1761
+ }
1762
+ }
1763
+ if (!declaredPolicyCodes.has(String(schedule.campusPolicyCode)) ||
1764
+ !declaredPolicyCodes.has(String(schedule.rulePolicyCode))) {
1765
+ fail('NATIVE_PUBLIC_SCHEDULE_POLICY_INVALID', `${policyPointer}/schedule`);
1766
+ }
1767
+ }
1768
+ const publicFilters = Object.prototype.hasOwnProperty.call(policy, 'publicFilters')
1769
+ ? boundedArray(policy.publicFilters, `${policyPointer}/publicFilters`, 16)
1770
+ : [];
1771
+ const filterFields = new Set();
1772
+ publicFilters.forEach((rawFilter, filterIndex) => {
1773
+ const filterPointer = `${policyPointer}/publicFilters/${filterIndex}`;
1774
+ const filter = object(rawFilter, filterPointer);
1775
+ exactKeys(filter, ['field', 'operator', 'value'], filterPointer);
1776
+ const field = fieldCodeValue(filter.field, `${filterPointer}/field`);
1777
+ if (filterFields.has(field)) {
1778
+ fail('NATIVE_PUBLIC_FILTER_DUPLICATE', `${filterPointer}/field`);
1779
+ }
1780
+ if (!fields.includes(field)) {
1781
+ fail('NATIVE_PUBLIC_FILTER_FIELD_INVALID', `${filterPointer}/field`);
1782
+ }
1783
+ equal(filter.operator, 'eq', `${filterPointer}/operator`);
1784
+ if (![
1785
+ 'boolean',
1786
+ 'text.short',
1787
+ 'text.long',
1788
+ 'number.integer',
1789
+ 'number.decimal',
1790
+ 'date',
1791
+ 'time',
1792
+ ].includes(declaredFields.get(field) || '')) {
1793
+ fail('NATIVE_PUBLIC_FILTER_FIELD_INVALID', `${filterPointer}/field`);
1794
+ }
1795
+ if (filter.value !== null &&
1796
+ typeof filter.value !== 'string' &&
1797
+ typeof filter.value !== 'number' &&
1798
+ typeof filter.value !== 'boolean') {
1799
+ fail('NATIVE_PUBLIC_FILTER_VALUE_INVALID', `${filterPointer}/value`);
1800
+ }
1801
+ filterFields.add(field);
1802
+ });
1803
+ const publicSubtableFields = Object.prototype.hasOwnProperty.call(policy, 'publicSubtableFields')
1804
+ ? object(policy.publicSubtableFields, `${policyPointer}/publicSubtableFields`)
1805
+ : null;
1806
+ if (publicSubtableFields) {
1807
+ if (Object.keys(publicSubtableFields).length > 64) {
1808
+ fail('NATIVE_PUBLIC_SUBTABLE_FIELDS_INVALID', `${policyPointer}/publicSubtableFields`);
1809
+ }
1810
+ for (const [parentFieldCode, rawChildFields] of Object.entries(publicSubtableFields)) {
1811
+ const parentField = resource.schema.fields.find((field) => String(field.code) === parentFieldCode);
1812
+ const childResourceCode = String(parentField?.subtable?.resourceCode || '');
1813
+ const childResource = resources.get(childResourceCode);
1814
+ const childFieldTypes = new Map((childResource?.schema?.fields || []).map((field) => [
1815
+ String(field.code),
1816
+ String(field.type),
1817
+ ]));
1818
+ const selected = uniqueStrings(rawChildFields, `${policyPointer}/publicSubtableFields/${parentFieldCode}`, 64);
1819
+ if (!fields.includes(parentFieldCode) ||
1820
+ declaredFields.get(parentFieldCode) !== 'subtable' ||
1821
+ !childResource ||
1822
+ selected.length < 1 ||
1823
+ selected.some(field => !childFieldTypes.has(field)) ||
1824
+ selected.some(field => childFieldTypes.get(field) === 'subtable') ||
1825
+ selected.some(field => childFieldTypes.get(field) === 'signature')) {
1826
+ fail('NATIVE_PUBLIC_SUBTABLE_FIELDS_INVALID', `${policyPointer}/publicSubtableFields/${parentFieldCode}`);
1827
+ }
1828
+ }
1829
+ }
1830
+ if (operations.some(operation => operation.startsWith('public.'))) {
1831
+ if (!Object.prototype.hasOwnProperty.call(policy, 'publicRecordFields')) {
1832
+ fail('NATIVE_PUBLIC_RECORD_FIELDS_REQUIRED', `${policyPointer}/publicRecordFields`);
1833
+ }
1834
+ const publicRecordFields = uniqueStrings(policy.publicRecordFields, `${policyPointer}/publicRecordFields`, 64);
1835
+ if (publicRecordFields.length < 1 ||
1836
+ publicRecordFields.some(field => !fields.includes(field)) ||
1837
+ publicRecordFields.some(field => declaredFields.get(field) === 'signature' ||
1838
+ (declaredFields.get(field) === 'subtable' &&
1839
+ !publicSubtableFields?.[field]))) {
1840
+ fail('NATIVE_PUBLIC_RECORD_FIELDS_INVALID', `${policyPointer}/publicRecordFields`);
1841
+ }
1842
+ }
1699
1843
  const requiredFields = Object.prototype.hasOwnProperty.call(policy, 'requiredFields')
1700
1844
  ? uniqueStrings(policy.requiredFields, `${policyPointer}/requiredFields`, 64)
1701
1845
  : [];
@@ -1709,6 +1853,10 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1709
1853
  if (operations.includes('create') && !nativeCreate) {
1710
1854
  fail('NATIVE_PUBLIC_CREATE_FIELDS_INCOMPLETE', `${policyPointer}/fields`);
1711
1855
  }
1856
+ if (operations.includes('create') &&
1857
+ !Object.prototype.hasOwnProperty.call(policy, 'draft')) {
1858
+ fail('NATIVE_PUBLIC_DRAFT_REQUIRED', `${policyPointer}/draft`);
1859
+ }
1712
1860
  if (operations.includes('create')) {
1713
1861
  for (const fieldCode of requiredCreateFields) {
1714
1862
  const missingSet = !fields.includes(fieldCode)
@@ -1752,7 +1900,6 @@ function validateAnonymousPublicAccess(value, config, pointer) {
1752
1900
  validationCodes.add(validationCode);
1753
1901
  });
1754
1902
  policyCodes.add(code);
1755
- routeCodes.add(routeCode);
1756
1903
  });
1757
1904
  }
1758
1905
  function validateAnonymousPublicAccessContract(value, pointer) {
@@ -1777,6 +1924,62 @@ function compileAnonymousPublicAccess(config) {
1777
1924
  ...(policy.ownRecordFields
1778
1925
  ? { ownRecordFields: uniqueSorted(policy.ownRecordFields) }
1779
1926
  : {}),
1927
+ ...(policy.publicRecordFields
1928
+ ? { publicRecordFields: uniqueSorted(policy.publicRecordFields) }
1929
+ : {}),
1930
+ ...(policy.publicFilters
1931
+ ? {
1932
+ publicFilters: policy.publicFilters
1933
+ .map((filter) => ({
1934
+ field: filter.field,
1935
+ operator: filter.operator,
1936
+ value: filter.value,
1937
+ }))
1938
+ .sort((left, right) => compareText(left.field, right.field)),
1939
+ }
1940
+ : {}),
1941
+ ...(policy.publicSubtableFields
1942
+ ? {
1943
+ publicSubtableFields: Object.fromEntries(Object.entries(policy.publicSubtableFields)
1944
+ .sort(([left], [right]) => left.localeCompare(right))
1945
+ .map(([field, childFields]) => [
1946
+ field,
1947
+ uniqueSorted(Array.isArray(childFields)
1948
+ ? childFields.map(String)
1949
+ : []),
1950
+ ])),
1951
+ }
1952
+ : {}),
1953
+ ...(policy.serverGeneratedFields
1954
+ ? {
1955
+ serverGeneratedFields: policy.serverGeneratedFields
1956
+ .map((generated) => ({
1957
+ field: generated.field,
1958
+ kind: generated.kind,
1959
+ }))
1960
+ .sort((left, right) => compareText(left.field, right.field)),
1961
+ }
1962
+ : {}),
1963
+ ...(policy.schedule
1964
+ ? {
1965
+ schedule: {
1966
+ campusPolicyCode: policy.schedule.campusPolicyCode,
1967
+ rulePolicyCode: policy.schedule.rulePolicyCode,
1968
+ campusField: policy.schedule.campusField,
1969
+ ruleCampusField: policy.schedule.ruleCampusField,
1970
+ ruleWeekdaysField: policy.schedule.ruleWeekdaysField,
1971
+ ruleOpenAtField: policy.schedule.ruleOpenAtField,
1972
+ ruleCloseAtField: policy.schedule.ruleCloseAtField,
1973
+ ruleSlotMinutesField: policy.schedule.ruleSlotMinutesField,
1974
+ ruleAdvanceHoursField: policy.schedule.ruleAdvanceHoursField,
1975
+ ruleAdvanceDaysField: policy.schedule.ruleAdvanceDaysField,
1976
+ campusEnabledField: policy.schedule.campusEnabledField,
1977
+ ruleEnabledField: policy.schedule.ruleEnabledField,
1978
+ dateField: policy.schedule.dateField,
1979
+ timeField: policy.schedule.timeField,
1980
+ },
1981
+ }
1982
+ : {}),
1780
1983
  ...(policy.draft ? { draft: { ...policy.draft } } : {}),
1781
1984
  ...(policy.validations
1782
1985
  ? {