@rippling/rippling-sdk 0.2.0-alpha.50 → 0.2.0-alpha.52
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.
- package/examples/functions/durable.ts +96 -0
- package/examples/manifest/existing-manifest-mutations/existing-manifest-mutations.ts +5 -0
- package/functions.d.mts +35 -0
- package/functions.d.mts.map +1 -1
- package/functions.d.ts +35 -0
- package/functions.d.ts.map +1 -1
- package/functions.js.map +1 -1
- package/functions.mjs.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.mts +19 -14
- package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.ts +19 -14
- package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
- package/lib/manifest/custom-object-page-layout.js +148 -77
- package/lib/manifest/custom-object-page-layout.js.map +1 -1
- package/lib/manifest/custom-object-page-layout.mjs +148 -77
- package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
- package/lib/manifest/existing-manifest-context.d.mts +1 -0
- package/lib/manifest/existing-manifest-context.d.mts.map +1 -1
- package/lib/manifest/existing-manifest-context.d.ts +1 -0
- package/lib/manifest/existing-manifest-context.d.ts.map +1 -1
- package/lib/manifest/existing-manifest-context.js +7 -6
- package/lib/manifest/existing-manifest-context.js.map +1 -1
- package/lib/manifest/existing-manifest-context.mjs +7 -6
- package/lib/manifest/existing-manifest-context.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/leave-balances.d.mts +10 -0
- package/resources/leave-balances.d.mts.map +1 -1
- package/resources/leave-balances.d.ts +10 -0
- package/resources/leave-balances.d.ts.map +1 -1
- package/src/functions.md +238 -0
- package/src/functions.ts +42 -0
- package/src/lib/manifest/custom-object-page-layout.ts +214 -112
- package/src/lib/manifest/existing-manifest-context.ts +11 -6
- package/src/resources/leave-balances.ts +12 -0
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/functions.ts
CHANGED
|
@@ -23,6 +23,36 @@ export class FunctionResponse {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export type FunctionEvent = Record<string, any>;
|
|
26
|
+
export type DurableDuration = number | string;
|
|
27
|
+
export type DurableTimestamp = number | Date | string;
|
|
28
|
+
export interface DurableWaitForEventOptions {
|
|
29
|
+
type: string;
|
|
30
|
+
timeout?: DurableDuration;
|
|
31
|
+
}
|
|
32
|
+
export interface DurableFailureOptions {
|
|
33
|
+
code?: string;
|
|
34
|
+
details?: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface DurableWaitForEventEventResult<T = unknown> {
|
|
37
|
+
kind: 'event';
|
|
38
|
+
waitName: string;
|
|
39
|
+
eventType: string;
|
|
40
|
+
payload: T;
|
|
41
|
+
}
|
|
42
|
+
export interface DurableWaitForEventTimeoutResult {
|
|
43
|
+
kind: 'timeout';
|
|
44
|
+
waitName: string;
|
|
45
|
+
eventType: string;
|
|
46
|
+
}
|
|
47
|
+
export interface DurableWaitForEventResultHelpers<T = unknown> {
|
|
48
|
+
isEvent(): this is DurableWaitForEventEventResult<T> & DurableWaitForEventResultHelpers<T>;
|
|
49
|
+
isTimeout(): this is DurableWaitForEventTimeoutResult & DurableWaitForEventResultHelpers<T>;
|
|
50
|
+
throwTimeout(this: DurableWaitForEventTimeoutResult & DurableWaitForEventResultHelpers<T>): never;
|
|
51
|
+
}
|
|
52
|
+
export type DurableWaitForEventResult<T = unknown> =
|
|
53
|
+
| (DurableWaitForEventEventResult<T> & DurableWaitForEventResultHelpers<T>)
|
|
54
|
+
| (DurableWaitForEventTimeoutResult & DurableWaitForEventResultHelpers<T>);
|
|
55
|
+
|
|
26
56
|
export interface FunctionContext {
|
|
27
57
|
env: {
|
|
28
58
|
// Set to the Rippling user's bearer token when the function is triggered by a user.
|
|
@@ -35,10 +65,22 @@ export interface FunctionContext {
|
|
|
35
65
|
function_id: string;
|
|
36
66
|
function_version_id: string;
|
|
37
67
|
run_id: string;
|
|
68
|
+
role_id?: string;
|
|
38
69
|
};
|
|
39
70
|
settings: Record<string, string | boolean | number>;
|
|
40
71
|
}
|
|
41
72
|
|
|
73
|
+
export interface DurableFunctionContext extends FunctionContext {
|
|
74
|
+
step<T>(name: string, fn: () => Promise<T> | T, opts?: unknown): Promise<T>;
|
|
75
|
+
sleep(name: string, duration: DurableDuration): Promise<void>;
|
|
76
|
+
sleepUntil(name: string, timestamp: DurableTimestamp): Promise<void>;
|
|
77
|
+
waitForEvent<T = unknown>(
|
|
78
|
+
name: string,
|
|
79
|
+
opts: DurableWaitForEventOptions,
|
|
80
|
+
): Promise<DurableWaitForEventResult<T>>;
|
|
81
|
+
fail(message: string, opts?: DurableFailureOptions): never;
|
|
82
|
+
}
|
|
83
|
+
|
|
42
84
|
export class DataBridgeRecord {
|
|
43
85
|
id: string;
|
|
44
86
|
version: string;
|
|
@@ -160,8 +160,8 @@ export interface FieldsSection extends PageLayoutSectionOptions {
|
|
|
160
160
|
type: 'fields_section';
|
|
161
161
|
/** Field section metadata for the fields displayed in this layout section. Required. */
|
|
162
162
|
section: CustomObjectFieldSection;
|
|
163
|
-
/** Stable layout section key.
|
|
164
|
-
key?:
|
|
163
|
+
/** Stable layout section key. Optional. Defaults to `section.getSectionId()`. */
|
|
164
|
+
key?: SectionKey;
|
|
165
165
|
/** Display name of the layout section. Optional. Defaults to the field section name. */
|
|
166
166
|
name?: string;
|
|
167
167
|
/** Fields to display in this section. Required. */
|
|
@@ -382,13 +382,13 @@ export interface AddCustomPageLayoutFieldOptions extends AddPageLayoutFieldOptio
|
|
|
382
382
|
* across the layout.
|
|
383
383
|
*/
|
|
384
384
|
tabKey?: string;
|
|
385
|
-
/** Field
|
|
386
|
-
section
|
|
387
|
-
/**
|
|
388
|
-
sectionKey?:
|
|
385
|
+
/** Field-section metadata used to find typed custom layout sections and assign field metadata. */
|
|
386
|
+
section?: CustomObjectFieldSection;
|
|
387
|
+
/** Layout section key. Optional escape hatch for ambiguous custom sections. */
|
|
388
|
+
sectionKey?: SectionKey;
|
|
389
389
|
/** Deprecated. Use `section`. */
|
|
390
390
|
fieldSection?: never;
|
|
391
|
-
/** Custom layout sections are selected by `section`; `sectionKey`
|
|
391
|
+
/** Custom layout sections are selected by `section`; `sectionKey` can disambiguate. */
|
|
392
392
|
systemSection?: false;
|
|
393
393
|
}
|
|
394
394
|
|
|
@@ -432,9 +432,9 @@ export interface RemoveCustomPageLayoutFieldOptions extends RemovePageLayoutFiel
|
|
|
432
432
|
tabKey?: string;
|
|
433
433
|
/** Limit removal to one field section. Optional. */
|
|
434
434
|
section?: CustomObjectFieldSection;
|
|
435
|
-
/**
|
|
436
|
-
sectionKey?:
|
|
437
|
-
/** Custom layout sections are selected by `section`; `sectionKey`
|
|
435
|
+
/** Limit removal to one layout section key. Optional. */
|
|
436
|
+
sectionKey?: SectionKey;
|
|
437
|
+
/** Custom layout sections are selected by `section`; `sectionKey` can disambiguate. */
|
|
438
438
|
systemSection?: false;
|
|
439
439
|
}
|
|
440
440
|
|
|
@@ -480,7 +480,7 @@ export interface RemovePageLayoutSectionOptions {
|
|
|
480
480
|
* more than once throws. Optional. @default true
|
|
481
481
|
*/
|
|
482
482
|
removeAll?: boolean;
|
|
483
|
-
/** Custom layout sections are selected by
|
|
483
|
+
/** Custom layout sections are selected by layout section key or `CustomObjectFieldSection`. */
|
|
484
484
|
systemSection?: false;
|
|
485
485
|
}
|
|
486
486
|
|
|
@@ -592,6 +592,8 @@ export interface CustomObjectPageLayoutLoadFromExistingOptions extends ExistingM
|
|
|
592
592
|
customObjectApiName?: string;
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
+
type ResolveFieldSectionId = (fieldRqlName: string) => string | undefined;
|
|
596
|
+
|
|
595
597
|
interface SerializedSectionField extends SectionFieldOptions {
|
|
596
598
|
fieldRqlName: string;
|
|
597
599
|
}
|
|
@@ -656,21 +658,25 @@ function isFieldRef(value: SectionFieldInput): value is SectionField {
|
|
|
656
658
|
return candidate != null && typeof candidate === 'object' && isField(candidate.field);
|
|
657
659
|
}
|
|
658
660
|
|
|
659
|
-
const
|
|
661
|
+
const LAYOUT_SECTION_KEY = Symbol('CustomObjectPageLayout.layoutSectionKey');
|
|
660
662
|
|
|
661
663
|
function isCustomObjectFieldSection(value: unknown): value is CustomObjectFieldSection {
|
|
662
664
|
return value instanceof CustomObjectFieldSection;
|
|
663
665
|
}
|
|
664
666
|
|
|
665
|
-
function
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
667
|
+
function explicitLayoutSectionKey(section: { key?: string }): string | undefined {
|
|
668
|
+
const record = section as unknown as Record<string, unknown>;
|
|
669
|
+
const key = section.key ?? record['layout_section_key'] ?? record['layoutSectionKey'];
|
|
670
|
+
return typeof key === 'string' ? key : undefined;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function attachLayoutSectionKey(
|
|
674
|
+
section: SerializedFieldsSection,
|
|
675
|
+
sectionKey: string,
|
|
676
|
+
): SerializedFieldsSection {
|
|
677
|
+
if (explicitLayoutSectionKey(section) == null) section.key = sectionKey;
|
|
678
|
+
Object.defineProperty(section, LAYOUT_SECTION_KEY, {
|
|
679
|
+
value: sectionKey,
|
|
674
680
|
enumerable: false,
|
|
675
681
|
configurable: true,
|
|
676
682
|
});
|
|
@@ -679,8 +685,8 @@ function attachFieldSectionId(section: SerializedFieldsSection, sectionId: strin
|
|
|
679
685
|
|
|
680
686
|
function layoutSectionKey(section: { key?: string }): string | undefined {
|
|
681
687
|
return (
|
|
682
|
-
section
|
|
683
|
-
(section as unknown as Record<typeof
|
|
688
|
+
explicitLayoutSectionKey(section) ??
|
|
689
|
+
(section as unknown as Record<typeof LAYOUT_SECTION_KEY, string | undefined>)[LAYOUT_SECTION_KEY]
|
|
684
690
|
);
|
|
685
691
|
}
|
|
686
692
|
|
|
@@ -688,11 +694,6 @@ function layoutSectionMatches(section: { key?: string }, sectionKey: string): bo
|
|
|
688
694
|
return layoutSectionKey(section) === sectionKey;
|
|
689
695
|
}
|
|
690
696
|
|
|
691
|
-
function inputFieldsSectionHasKey(input: FieldsSection): boolean {
|
|
692
|
-
const record = input as unknown as Record<string, unknown>;
|
|
693
|
-
return Object.prototype.hasOwnProperty.call(record, 'key') && record['key'] != null;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
697
|
function isSerializedFieldsSection(section: SerializedPageLayoutSection): section is SerializedFieldsSection {
|
|
697
698
|
return section.type === 'fields_section';
|
|
698
699
|
}
|
|
@@ -783,23 +784,20 @@ function normalizeSectionField(
|
|
|
783
784
|
|
|
784
785
|
function normalizeFieldsSection(customObject: CustomObject, input: FieldsSection): SerializedFieldsSection {
|
|
785
786
|
assertSectionBelongsTo(customObject, input.section);
|
|
786
|
-
const
|
|
787
|
-
if (inputFieldsSectionHasKey(input)) {
|
|
788
|
-
throw new Error('CustomObjectPageLayout fields_section key is not supported. Omit key.');
|
|
789
|
-
}
|
|
787
|
+
const sectionKey = input.key ?? input.section.getSectionId();
|
|
790
788
|
const name = input.name ?? input.section.getName();
|
|
791
789
|
const normalized = copySectionOptions<SerializedFieldsSection>(
|
|
792
790
|
{
|
|
793
791
|
name,
|
|
794
792
|
type: 'fields_section',
|
|
795
793
|
fields: input.fields.map((field) =>
|
|
796
|
-
normalizeSectionField(customObject, input.section,
|
|
794
|
+
normalizeSectionField(customObject, input.section, sectionKey, field),
|
|
797
795
|
),
|
|
798
796
|
},
|
|
799
797
|
input,
|
|
800
798
|
);
|
|
801
799
|
if (input.layout != null) normalized.layout = input.layout;
|
|
802
|
-
return
|
|
800
|
+
return attachLayoutSectionKey(normalized, sectionKey);
|
|
803
801
|
}
|
|
804
802
|
|
|
805
803
|
function normalizeSection(
|
|
@@ -1010,6 +1008,11 @@ interface MutableFieldContainer {
|
|
|
1010
1008
|
label: string;
|
|
1011
1009
|
}
|
|
1012
1010
|
|
|
1011
|
+
interface FieldSectionLayoutSection {
|
|
1012
|
+
tabKey: string;
|
|
1013
|
+
sectionKey: string;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1013
1016
|
function isTabWithSections(tab: SerializedPageLayoutTab): tab is SerializedTabWithSections {
|
|
1014
1017
|
return tab.type === 'tab_with_sections';
|
|
1015
1018
|
}
|
|
@@ -1040,72 +1043,126 @@ function fieldRqlNameOf(entry: unknown): string | undefined {
|
|
|
1040
1043
|
if (typeof entry === 'string') return entry;
|
|
1041
1044
|
if (entry == null || typeof entry !== 'object') return undefined;
|
|
1042
1045
|
const record = entry as Record<string, any>;
|
|
1043
|
-
const fieldRqlName =
|
|
1046
|
+
const fieldRqlName =
|
|
1047
|
+
record['fieldRqlName'] ?? record['field_rql_name'] ?? record['apiName'] ?? record['api_name'];
|
|
1044
1048
|
return typeof fieldRqlName === 'string' ? fieldRqlName : undefined;
|
|
1045
1049
|
}
|
|
1046
1050
|
|
|
1047
|
-
function
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1051
|
+
function addFieldSectionLayoutSection(
|
|
1052
|
+
index: Map<string, FieldSectionLayoutSection[]>,
|
|
1053
|
+
fieldSectionId: string,
|
|
1054
|
+
entry: FieldSectionLayoutSection,
|
|
1055
|
+
): void {
|
|
1056
|
+
const entries = index.get(fieldSectionId) ?? [];
|
|
1057
|
+
if (
|
|
1058
|
+
!entries.some((existing) => existing.tabKey === entry.tabKey && existing.sectionKey === entry.sectionKey)
|
|
1059
|
+
) {
|
|
1060
|
+
entries.push(entry);
|
|
1061
|
+
}
|
|
1062
|
+
index.set(fieldSectionId, entries);
|
|
1052
1063
|
}
|
|
1053
1064
|
|
|
1054
|
-
function
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
+
function indexFieldsSectionInput(
|
|
1066
|
+
index: Map<string, FieldSectionLayoutSection[]>,
|
|
1067
|
+
tabKey: string,
|
|
1068
|
+
section: FieldsSection,
|
|
1069
|
+
): void {
|
|
1070
|
+
addFieldSectionLayoutSection(index, section.section.getSectionId(), {
|
|
1071
|
+
tabKey,
|
|
1072
|
+
sectionKey: section.key ?? section.section.getSectionId(),
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function indexPageLayoutSectionInput(
|
|
1077
|
+
index: Map<string, FieldSectionLayoutSection[]>,
|
|
1078
|
+
tabKey: string,
|
|
1079
|
+
section: PageLayoutSection,
|
|
1080
|
+
): void {
|
|
1081
|
+
if (section.type === 'fields_section') indexFieldsSectionInput(index, tabKey, section);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
function buildFieldSectionLayoutIndexFromProps(
|
|
1085
|
+
tabEdits: TabEdits | undefined,
|
|
1086
|
+
): Map<string, FieldSectionLayoutSection[]> {
|
|
1087
|
+
const index = new Map<string, FieldSectionLayoutSection[]>();
|
|
1088
|
+
if (tabEdits == null) return index;
|
|
1065
1089
|
|
|
1066
|
-
|
|
1067
|
-
if (
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1090
|
+
for (const tab of tabEdits.newTabs ?? []) {
|
|
1091
|
+
if (tab.type !== 'tab_with_sections') continue;
|
|
1092
|
+
for (const section of tab.sections) indexPageLayoutSectionInput(index, tab.key, section);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
for (const [tabKey, tabEdit] of Object.entries(tabEdits.systemTabEdits ?? {})) {
|
|
1096
|
+
for (const section of tabEdit.newSections ?? []) {
|
|
1097
|
+
indexPageLayoutSectionInput(index, tabKey, section);
|
|
1098
|
+
}
|
|
1070
1099
|
}
|
|
1071
|
-
|
|
1100
|
+
|
|
1101
|
+
return index;
|
|
1072
1102
|
}
|
|
1073
1103
|
|
|
1074
|
-
function
|
|
1104
|
+
function indexLoadedFieldsSection(
|
|
1105
|
+
index: Map<string, FieldSectionLayoutSection[]>,
|
|
1106
|
+
tabKey: string,
|
|
1075
1107
|
section: SerializedFieldsSection,
|
|
1076
|
-
resolveFieldSectionId
|
|
1077
|
-
):
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
);
|
|
1108
|
+
resolveFieldSectionId: ResolveFieldSectionId | undefined,
|
|
1109
|
+
): void {
|
|
1110
|
+
const sectionKey = layoutSectionKey(section);
|
|
1111
|
+
if (sectionKey == null || resolveFieldSectionId == null) return;
|
|
1112
|
+
|
|
1113
|
+
for (const field of section.fields ?? []) {
|
|
1114
|
+
const fieldRqlName = fieldRqlNameOf(field);
|
|
1115
|
+
if (fieldRqlName == null) continue;
|
|
1116
|
+
const fieldSectionId = resolveFieldSectionId(fieldRqlName);
|
|
1117
|
+
if (fieldSectionId != null) {
|
|
1118
|
+
addFieldSectionLayoutSection(index, fieldSectionId, { tabKey, sectionKey });
|
|
1119
|
+
}
|
|
1085
1120
|
}
|
|
1086
|
-
const sectionId = explicitSectionId ?? inferredSectionId;
|
|
1087
|
-
return sectionId == null ? section : attachFieldSectionId(section, sectionId);
|
|
1088
1121
|
}
|
|
1089
1122
|
|
|
1090
|
-
function
|
|
1123
|
+
function buildFieldSectionLayoutIndexFromLoadedTabEdits(
|
|
1091
1124
|
tabEdits: SerializedTabEdits,
|
|
1092
|
-
resolveFieldSectionId
|
|
1093
|
-
):
|
|
1125
|
+
resolveFieldSectionId: ResolveFieldSectionId | undefined,
|
|
1126
|
+
): Map<string, FieldSectionLayoutSection[]> {
|
|
1127
|
+
const index = new Map<string, FieldSectionLayoutSection[]>();
|
|
1128
|
+
|
|
1129
|
+
for (const tab of tabEdits.newTabs ?? []) {
|
|
1130
|
+
if (!isTabWithSections(tab)) continue;
|
|
1131
|
+
for (const section of tab.sections) {
|
|
1132
|
+
if (isSerializedFieldsSection(section)) {
|
|
1133
|
+
indexLoadedFieldsSection(index, tab.key, section, resolveFieldSectionId);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
for (const [tabKey, tabEdit] of Object.entries(tabEdits.systemTabEdits ?? {})) {
|
|
1139
|
+
for (const section of tabEdit.newSections ?? []) {
|
|
1140
|
+
if (isSerializedFieldsSection(section)) {
|
|
1141
|
+
indexLoadedFieldsSection(index, tabKey, section, resolveFieldSectionId);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
return index;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
function annotateLoadedFieldsSection(section: SerializedFieldsSection): SerializedFieldsSection {
|
|
1150
|
+
const sectionKey = layoutSectionKey(section);
|
|
1151
|
+
return sectionKey == null ? section : attachLayoutSectionKey(section, sectionKey);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function annotateLoadedTabEdits(tabEdits: SerializedTabEdits): SerializedTabEdits {
|
|
1094
1155
|
for (const tab of tabEdits.newTabs ?? []) {
|
|
1095
1156
|
if (!isTabWithSections(tab)) continue;
|
|
1096
1157
|
tab.sections = tab.sections.map((section) =>
|
|
1097
|
-
isSerializedFieldsSection(section) ?
|
|
1098
|
-
annotateLoadedFieldsSection(section, resolveFieldSectionId)
|
|
1099
|
-
: section,
|
|
1158
|
+
isSerializedFieldsSection(section) ? annotateLoadedFieldsSection(section) : section,
|
|
1100
1159
|
);
|
|
1101
1160
|
}
|
|
1102
1161
|
|
|
1103
1162
|
for (const tabEdit of Object.values(tabEdits.systemTabEdits ?? {})) {
|
|
1104
1163
|
if (tabEdit.newSections != null) {
|
|
1105
1164
|
tabEdit.newSections = tabEdit.newSections.map((section) =>
|
|
1106
|
-
isSerializedFieldsSection(section) ?
|
|
1107
|
-
annotateLoadedFieldsSection(section, resolveFieldSectionId)
|
|
1108
|
-
: section,
|
|
1165
|
+
isSerializedFieldsSection(section) ? annotateLoadedFieldsSection(section) : section,
|
|
1109
1166
|
);
|
|
1110
1167
|
}
|
|
1111
1168
|
}
|
|
@@ -1228,6 +1285,7 @@ export class CustomObjectPageLayout {
|
|
|
1228
1285
|
private readonly _headerEdits: SerializedHeaderEdits;
|
|
1229
1286
|
private readonly _visibilityConditions: VisibilityConditions;
|
|
1230
1287
|
private readonly _blueprintKey: string | undefined;
|
|
1288
|
+
private readonly _fieldSectionLayoutSections: Map<string, FieldSectionLayoutSection[]>;
|
|
1231
1289
|
|
|
1232
1290
|
/**
|
|
1233
1291
|
* @param customObject - The custom object this layout belongs to.
|
|
@@ -1242,6 +1300,7 @@ export class CustomObjectPageLayout {
|
|
|
1242
1300
|
this._customObjectApiName = customObject.getApiName();
|
|
1243
1301
|
this._name = props.name;
|
|
1244
1302
|
this._tabEdits = normalizeTabEdits(customObject, props.tabEdits ?? {});
|
|
1303
|
+
this._fieldSectionLayoutSections = buildFieldSectionLayoutIndexFromProps(props.tabEdits);
|
|
1245
1304
|
this._headerEdits = normalizeHeaderEdits(props.headerEdits ?? {});
|
|
1246
1305
|
this._visibilityConditions = props.visibilityConditions ?? {};
|
|
1247
1306
|
this._blueprintKey = props.blueprintKey;
|
|
@@ -1266,9 +1325,10 @@ export class CustomObjectPageLayout {
|
|
|
1266
1325
|
static _fromExistingComponent(
|
|
1267
1326
|
customObject: CustomObject,
|
|
1268
1327
|
component: Record<string, any>,
|
|
1269
|
-
resolveFieldSectionId?:
|
|
1328
|
+
resolveFieldSectionId?: ResolveFieldSectionId,
|
|
1270
1329
|
): CustomObjectPageLayout {
|
|
1271
1330
|
const layout = Object.create(CustomObjectPageLayout.prototype) as CustomObjectPageLayout;
|
|
1331
|
+
const tabEdits = annotateLoadedTabEdits(cloneJson(component['tab_edits'] ?? {}));
|
|
1272
1332
|
Object.assign(layout as Record<string, any>, {
|
|
1273
1333
|
_layoutId: requireStringComponentField(component, 'layout_id', CustomObjectPageLayout.componentType),
|
|
1274
1334
|
_apiName: requireStringComponentField(component, 'api_name', CustomObjectPageLayout.componentType),
|
|
@@ -1278,10 +1338,14 @@ export class CustomObjectPageLayout {
|
|
|
1278
1338
|
CustomObjectPageLayout.componentType,
|
|
1279
1339
|
),
|
|
1280
1340
|
_name: requireStringComponentField(component, 'name', CustomObjectPageLayout.componentType),
|
|
1281
|
-
_tabEdits:
|
|
1341
|
+
_tabEdits: tabEdits,
|
|
1282
1342
|
_headerEdits: cloneJson(component['header_edits'] ?? {}),
|
|
1283
1343
|
_visibilityConditions: cloneJson(component['visibility_conditions'] ?? {}),
|
|
1284
1344
|
_blueprintKey: component['blueprint_key'],
|
|
1345
|
+
_fieldSectionLayoutSections: buildFieldSectionLayoutIndexFromLoadedTabEdits(
|
|
1346
|
+
tabEdits,
|
|
1347
|
+
resolveFieldSectionId,
|
|
1348
|
+
),
|
|
1285
1349
|
});
|
|
1286
1350
|
customObject._register(layout);
|
|
1287
1351
|
return layout;
|
|
@@ -1322,8 +1386,8 @@ export class CustomObjectPageLayout {
|
|
|
1322
1386
|
* Adds multiple fields to an existing layout section.
|
|
1323
1387
|
*
|
|
1324
1388
|
* Fields are inserted in the order provided. For custom layout sections,
|
|
1325
|
-
* pass `
|
|
1326
|
-
*
|
|
1389
|
+
* pass `section` to choose the typed field section. Use `sectionKey` only
|
|
1390
|
+
* when a field section maps to multiple layout sections.
|
|
1327
1391
|
*/
|
|
1328
1392
|
addFields(fields: Field[], options: AddPageLayoutFieldsOptions): this {
|
|
1329
1393
|
if (fields.length === 0) return this;
|
|
@@ -1346,25 +1410,25 @@ export class CustomObjectPageLayout {
|
|
|
1346
1410
|
return this.ensureSystemSectionFieldContainer(options.tabKey, options.sectionKey);
|
|
1347
1411
|
})()
|
|
1348
1412
|
: (() => {
|
|
1349
|
-
assertOptionAbsent(
|
|
1350
|
-
options,
|
|
1351
|
-
'sectionKey',
|
|
1352
|
-
'CustomObjectPageLayout addField sectionKey is only supported with systemSection: true. ' +
|
|
1353
|
-
'Pass section: CustomObjectFieldSection for custom layout sections.',
|
|
1354
|
-
);
|
|
1355
1413
|
assertOptionAbsent(
|
|
1356
1414
|
options,
|
|
1357
1415
|
'fieldSection',
|
|
1358
1416
|
'CustomObjectPageLayout addField fieldSection is no longer supported. ' +
|
|
1359
1417
|
'Pass section: CustomObjectFieldSection instead.',
|
|
1360
1418
|
);
|
|
1361
|
-
const section =
|
|
1362
|
-
options.section
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1419
|
+
const section =
|
|
1420
|
+
options.section == null ?
|
|
1421
|
+
undefined
|
|
1422
|
+
: requireFieldSectionOption(options.section, 'CustomObjectPageLayout addField section');
|
|
1423
|
+
if (section != null) {
|
|
1424
|
+
this.assertFieldSectionBelongsToLayout(section);
|
|
1425
|
+
fieldSectionForMutation = section;
|
|
1426
|
+
}
|
|
1427
|
+
const sectionKey =
|
|
1428
|
+
options.sectionKey ??
|
|
1429
|
+
(section == null ? undefined : this.layoutSectionKeyForFieldSection(options.tabKey, section)) ??
|
|
1430
|
+
section?.getSectionId();
|
|
1431
|
+
return this.requireOneFieldContainer(options.tabKey, sectionKey, section);
|
|
1368
1432
|
})();
|
|
1369
1433
|
const entries = fields.map((field) =>
|
|
1370
1434
|
this.serializeFieldForMutation(
|
|
@@ -1413,19 +1477,15 @@ export class CustomObjectPageLayout {
|
|
|
1413
1477
|
);
|
|
1414
1478
|
sectionKey = options.sectionKey;
|
|
1415
1479
|
} else {
|
|
1416
|
-
|
|
1417
|
-
options,
|
|
1418
|
-
'sectionKey',
|
|
1419
|
-
'CustomObjectPageLayout removeField sectionKey is only supported with systemSection: true. ' +
|
|
1420
|
-
'Pass section: CustomObjectFieldSection for custom layout sections.',
|
|
1421
|
-
);
|
|
1480
|
+
sectionKey = options.sectionKey;
|
|
1422
1481
|
if (options.section != null) {
|
|
1423
1482
|
const section = requireFieldSectionOption(
|
|
1424
1483
|
options.section,
|
|
1425
1484
|
'CustomObjectPageLayout removeField section',
|
|
1426
1485
|
);
|
|
1427
1486
|
this.assertFieldSectionBelongsToLayout(section);
|
|
1428
|
-
sectionKey
|
|
1487
|
+
sectionKey ??= this.layoutSectionKeyForFieldSection(options.tabKey, section);
|
|
1488
|
+
sectionKey ??= section.getSectionId();
|
|
1429
1489
|
}
|
|
1430
1490
|
}
|
|
1431
1491
|
|
|
@@ -1499,6 +1559,9 @@ export class CustomObjectPageLayout {
|
|
|
1499
1559
|
const sectionsOrder = this.ensureSystemSectionsOrder(tabEdit);
|
|
1500
1560
|
insertAt(sections, normalized, options.position);
|
|
1501
1561
|
insertUniqueAt(sectionsOrder, layoutSectionKey(normalized), options.position);
|
|
1562
|
+
if (section.type === 'fields_section') {
|
|
1563
|
+
this.rememberFieldSectionLayoutSection(section.section, options.tabKey, layoutSectionKey(normalized));
|
|
1564
|
+
}
|
|
1502
1565
|
return this;
|
|
1503
1566
|
}
|
|
1504
1567
|
|
|
@@ -1516,6 +1579,9 @@ export class CustomObjectPageLayout {
|
|
|
1516
1579
|
options.allowDuplicate,
|
|
1517
1580
|
);
|
|
1518
1581
|
insertAt(tab.sections, normalized, options.position);
|
|
1582
|
+
if (section.type === 'fields_section') {
|
|
1583
|
+
this.rememberFieldSectionLayoutSection(section.section, tab.key, layoutSectionKey(normalized));
|
|
1584
|
+
}
|
|
1519
1585
|
return this;
|
|
1520
1586
|
}
|
|
1521
1587
|
|
|
@@ -1526,6 +1592,7 @@ export class CustomObjectPageLayout {
|
|
|
1526
1592
|
* `systemSectionEdits[sectionKey].deleted`.
|
|
1527
1593
|
*/
|
|
1528
1594
|
removeSection(section: CustomObjectFieldSection, options?: RemovePageLayoutSectionOptions): this;
|
|
1595
|
+
removeSection(sectionKey: string, options?: RemovePageLayoutSectionOptions): this;
|
|
1529
1596
|
removeSection(sectionKey: string, options: RemoveSystemPageLayoutSectionOptions): this;
|
|
1530
1597
|
removeSection(
|
|
1531
1598
|
sectionOrKey: CustomObjectFieldSection | string,
|
|
@@ -1547,9 +1614,14 @@ export class CustomObjectPageLayout {
|
|
|
1547
1614
|
return this;
|
|
1548
1615
|
}
|
|
1549
1616
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1617
|
+
let sectionKey: string;
|
|
1618
|
+
if (typeof sectionOrKey === 'string') {
|
|
1619
|
+
sectionKey = sectionOrKey;
|
|
1620
|
+
} else {
|
|
1621
|
+
const section = requireFieldSectionOption(sectionOrKey, 'CustomObjectPageLayout removeSection section');
|
|
1622
|
+
this.assertFieldSectionBelongsToLayout(section);
|
|
1623
|
+
sectionKey = this.layoutSectionKeyForFieldSection(options.tabKey, section) ?? section.getSectionId();
|
|
1624
|
+
}
|
|
1553
1625
|
const matches: Array<{
|
|
1554
1626
|
sections: SerializedPageLayoutSection[];
|
|
1555
1627
|
section: SerializedFieldsSection;
|
|
@@ -1609,6 +1681,10 @@ export class CustomObjectPageLayout {
|
|
|
1609
1681
|
const tabsOrder = this.ensureTabsOrder();
|
|
1610
1682
|
insertAt(newTabs, normalized, options.position);
|
|
1611
1683
|
insertUniqueAt(tabsOrder, normalized.key, options.position);
|
|
1684
|
+
if (tab.type === 'tab_with_sections') {
|
|
1685
|
+
for (const section of tab.sections)
|
|
1686
|
+
indexPageLayoutSectionInput(this._fieldSectionLayoutSections, tab.key, section);
|
|
1687
|
+
}
|
|
1612
1688
|
return this;
|
|
1613
1689
|
}
|
|
1614
1690
|
|
|
@@ -1805,23 +1881,20 @@ export class CustomObjectPageLayout {
|
|
|
1805
1881
|
|
|
1806
1882
|
this.assertFieldSectionBelongsToLayout(input.section);
|
|
1807
1883
|
|
|
1808
|
-
const
|
|
1809
|
-
if (inputFieldsSectionHasKey(input)) {
|
|
1810
|
-
throw new Error('CustomObjectPageLayout fields_section key is not supported. Omit key.');
|
|
1811
|
-
}
|
|
1884
|
+
const sectionKey = input.key ?? input.section.getSectionId();
|
|
1812
1885
|
const name = input.name ?? input.section.getName();
|
|
1813
1886
|
const normalized = copySectionOptions<SerializedFieldsSection>(
|
|
1814
1887
|
{
|
|
1815
1888
|
name,
|
|
1816
1889
|
type: 'fields_section',
|
|
1817
1890
|
fields: input.fields.map((field) =>
|
|
1818
|
-
this.normalizeSectionFieldForMutation(input.section,
|
|
1891
|
+
this.normalizeSectionFieldForMutation(input.section, sectionKey, field),
|
|
1819
1892
|
),
|
|
1820
1893
|
},
|
|
1821
1894
|
input,
|
|
1822
1895
|
);
|
|
1823
1896
|
if (input.layout != null) normalized.layout = input.layout;
|
|
1824
|
-
return
|
|
1897
|
+
return attachLayoutSectionKey(normalized, sectionKey);
|
|
1825
1898
|
}
|
|
1826
1899
|
|
|
1827
1900
|
private normalizeTabForMutation(tab: PageLayoutTab): SerializedPageLayoutTab {
|
|
@@ -1884,6 +1957,35 @@ export class CustomObjectPageLayout {
|
|
|
1884
1957
|
}
|
|
1885
1958
|
}
|
|
1886
1959
|
|
|
1960
|
+
private rememberFieldSectionLayoutSection(
|
|
1961
|
+
section: CustomObjectFieldSection,
|
|
1962
|
+
tabKey: string,
|
|
1963
|
+
sectionKey: string | undefined,
|
|
1964
|
+
): void {
|
|
1965
|
+
if (sectionKey == null) return;
|
|
1966
|
+
addFieldSectionLayoutSection(this._fieldSectionLayoutSections, section.getSectionId(), {
|
|
1967
|
+
tabKey,
|
|
1968
|
+
sectionKey,
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
private layoutSectionKeyForFieldSection(
|
|
1973
|
+
tabKey: string | undefined,
|
|
1974
|
+
section: CustomObjectFieldSection,
|
|
1975
|
+
): string | undefined {
|
|
1976
|
+
const entries = this._fieldSectionLayoutSections.get(section.getSectionId()) ?? [];
|
|
1977
|
+
const matches = tabKey == null ? entries : entries.filter((entry) => entry.tabKey === tabKey);
|
|
1978
|
+
const sectionKeys = [...new Set(matches.map((entry) => entry.sectionKey))];
|
|
1979
|
+
if (sectionKeys.length === 0) return undefined;
|
|
1980
|
+
if (sectionKeys.length === 1) return sectionKeys[0];
|
|
1981
|
+
|
|
1982
|
+
const scope = tabKey == null ? '' : ` in tab "${tabKey}"`;
|
|
1983
|
+
throw new Error(
|
|
1984
|
+
`CustomObjectPageLayout field section "${section.getSectionId()}" maps to multiple layout sections${scope}: ` +
|
|
1985
|
+
`${sectionKeys.join(', ')}. Pass sectionKey to choose one.`,
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1887
1989
|
private requireOneFieldContainer(
|
|
1888
1990
|
tabKey: string | undefined,
|
|
1889
1991
|
sectionKey: string | undefined,
|
|
@@ -1913,8 +2015,8 @@ export class CustomObjectPageLayout {
|
|
|
1913
2015
|
const container = unkeyedContainers[0] as MutableFieldContainer & {
|
|
1914
2016
|
section: SerializedFieldsSection;
|
|
1915
2017
|
};
|
|
1916
|
-
|
|
1917
|
-
container.sectionKey =
|
|
2018
|
+
attachLayoutSectionKey(container.section, sectionKey);
|
|
2019
|
+
container.sectionKey = sectionKey;
|
|
1918
2020
|
container.label = `${container.tabKey}.${container.sectionKey}`;
|
|
1919
2021
|
return container;
|
|
1920
2022
|
}
|
|
@@ -417,11 +417,9 @@ export class ExistingManifestContext {
|
|
|
417
417
|
return this.getOrLoad(cacheKey, () => {
|
|
418
418
|
const customObject = this.loadCustomObject(customObjectApiName);
|
|
419
419
|
this.loadPageLayoutDependencies(customObjectApiName, component);
|
|
420
|
-
return CustomObjectPageLayout._fromExistingComponent(customObject, component, (fieldApiName) =>
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
return typeof sectionId === 'string' ? sectionId : undefined;
|
|
424
|
-
});
|
|
420
|
+
return CustomObjectPageLayout._fromExistingComponent(customObject, component, (fieldApiName) =>
|
|
421
|
+
this.fieldSectionIdForField(customObjectApiName, fieldApiName),
|
|
422
|
+
);
|
|
425
423
|
});
|
|
426
424
|
}
|
|
427
425
|
|
|
@@ -522,6 +520,12 @@ export class ExistingManifestContext {
|
|
|
522
520
|
return matches[0] as Record<string, any>;
|
|
523
521
|
}
|
|
524
522
|
|
|
523
|
+
private fieldSectionIdForField(customObjectApiName: string, fieldApiName: string): string | undefined {
|
|
524
|
+
const field = this.findFieldComponent(customObjectApiName, fieldApiName);
|
|
525
|
+
const section = field?.['section'];
|
|
526
|
+
return typeof section === 'string' && section.length > 0 ? section : undefined;
|
|
527
|
+
}
|
|
528
|
+
|
|
525
529
|
private loadFieldReferenceComponent(component: Record<string, any>): Field {
|
|
526
530
|
const cacheKey = this.cacheKey(
|
|
527
531
|
'CUSTOM_OBJECT_FIELD_REF',
|
|
@@ -670,7 +674,8 @@ export class ExistingManifestContext {
|
|
|
670
674
|
if (field == null || typeof field !== 'object') return;
|
|
671
675
|
const record = field as Record<string, any>;
|
|
672
676
|
const fieldSection = record['fieldSection'] ?? record['field_section'];
|
|
673
|
-
const fieldApiName =
|
|
677
|
+
const fieldApiName =
|
|
678
|
+
record['fieldRqlName'] ?? record['field_rql_name'] ?? record['apiName'] ?? record['api_name'];
|
|
674
679
|
if (typeof fieldSection === 'string') {
|
|
675
680
|
const fieldLabel = typeof fieldApiName === 'string' ? ` ${fieldApiName}` : '';
|
|
676
681
|
this.loadRequiredFieldSection(
|