salesforce-metadata-mcp 2.2.0 → 2.4.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.
- package/CHANGELOG.md +104 -0
- package/README.md +113 -3
- package/dist/index.js +4 -4
- package/dist/schemas/index.js +382 -5
- package/dist/services/salesforce.js +1025 -0
- package/dist/tools/audit.js +96 -0
- package/dist/tools/data.js +33 -2
- package/dist/tools/deployment.js +22 -2
- package/dist/tools/einstein.js +79 -0
- package/dist/tools/index.js +8 -0
- package/dist/tools/integrations.js +39 -2
- package/dist/tools/omnichannel.js +279 -0
- package/dist/tools/omnistudio.js +280 -0
- package/dist/tools/ui.js +32 -2
- package/package.json +26 -3
|
@@ -2653,4 +2653,1029 @@ export async function setExperienceSiteBranding(auth, params) {
|
|
|
2653
2653
|
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2654
2654
|
}
|
|
2655
2655
|
}
|
|
2656
|
+
// ─── v2.3.0 OmniStudio Service Functions ─────────────────────────────────────
|
|
2657
|
+
function buildFlexCardDefinition(params) {
|
|
2658
|
+
return {
|
|
2659
|
+
name: params.cardName,
|
|
2660
|
+
sObjectType: params.objectApiName ?? "",
|
|
2661
|
+
dataSource: params.dataSourceType !== "None" ? {
|
|
2662
|
+
type: params.dataSourceType,
|
|
2663
|
+
value: params.dataSourceName ?? "",
|
|
2664
|
+
} : null,
|
|
2665
|
+
elements: (params.fields ?? []).map(f => ({
|
|
2666
|
+
type: "text",
|
|
2667
|
+
name: `field_${f.fieldName}`,
|
|
2668
|
+
label: f.label,
|
|
2669
|
+
fieldName: f.fieldName,
|
|
2670
|
+
dataType: f.type ?? "Text",
|
|
2671
|
+
})),
|
|
2672
|
+
actions: (params.actions ?? []).map(a => ({
|
|
2673
|
+
type: a.type,
|
|
2674
|
+
label: a.label,
|
|
2675
|
+
target: a.target,
|
|
2676
|
+
})),
|
|
2677
|
+
states: (params.states ?? [{ name: "Default", conditionType: "ALL", conditions: [] }]).map(s => ({
|
|
2678
|
+
name: s.name,
|
|
2679
|
+
conditionType: s.conditionType,
|
|
2680
|
+
conditions: s.conditions ?? [],
|
|
2681
|
+
})),
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
export async function createFlexCard(auth, params) {
|
|
2685
|
+
try {
|
|
2686
|
+
const definition = buildFlexCardDefinition(params);
|
|
2687
|
+
const xml = `<met:metadata xsi:type="met:OmniUiCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2688
|
+
<met:fullName>${x(params.cardName)}</met:fullName>
|
|
2689
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
2690
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2691
|
+
<met:omniUiCardType>Child</met:omniUiCardType>
|
|
2692
|
+
<met:versionNumber>1</met:versionNumber>
|
|
2693
|
+
<met:isActive>false</met:isActive>
|
|
2694
|
+
<met:omniUiCardDefinition>${x(JSON.stringify(definition))}</met:omniUiCardDefinition>
|
|
2695
|
+
</met:metadata>`;
|
|
2696
|
+
return await upsertMetadata(auth, xml);
|
|
2697
|
+
} catch (err) {
|
|
2698
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
export async function updateFlexCard(auth, params) {
|
|
2702
|
+
try {
|
|
2703
|
+
const existing = await readMetadataItem(auth, "OmniUiCard", params.cardName);
|
|
2704
|
+
if (!existing.success) return existing;
|
|
2705
|
+
const defMatch = existing.rawXml.match(/<omniUiCardDefinition[^>]*>([\s\S]*?)<\/omniUiCardDefinition>/i);
|
|
2706
|
+
let definition = {};
|
|
2707
|
+
if (defMatch) {
|
|
2708
|
+
try { definition = JSON.parse(defMatch[1].replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")); }
|
|
2709
|
+
catch { /* use empty definition */ }
|
|
2710
|
+
}
|
|
2711
|
+
const merged = {
|
|
2712
|
+
...definition,
|
|
2713
|
+
...(params.dataSourceType ? { dataSource: { type: params.dataSourceType, value: params.dataSourceName ?? "" } } : {}),
|
|
2714
|
+
...(params.fields ? { elements: params.fields.map(f => ({ type: "text", name: `field_${f.fieldName}`, label: f.label, fieldName: f.fieldName })) } : {}),
|
|
2715
|
+
...(params.actions ? { actions: params.actions } : {}),
|
|
2716
|
+
...(params.states ? { states: params.states } : {}),
|
|
2717
|
+
};
|
|
2718
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2719
|
+
const currentLabel = labelMatch?.[1] ?? params.cardName;
|
|
2720
|
+
const xml = `<met:metadata xsi:type="met:OmniUiCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2721
|
+
<met:fullName>${x(params.cardName)}</met:fullName>
|
|
2722
|
+
<met:masterLabel>${x(params.label ?? currentLabel)}</met:masterLabel>
|
|
2723
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2724
|
+
<met:omniUiCardType>Child</met:omniUiCardType>
|
|
2725
|
+
<met:isActive>false</met:isActive>
|
|
2726
|
+
<met:omniUiCardDefinition>${x(JSON.stringify(merged))}</met:omniUiCardDefinition>
|
|
2727
|
+
</met:metadata>`;
|
|
2728
|
+
return await upsertMetadata(auth, xml);
|
|
2729
|
+
} catch (err) {
|
|
2730
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
export async function activateFlexCard(auth, params) {
|
|
2734
|
+
try {
|
|
2735
|
+
const existing = await readMetadataItem(auth, "OmniUiCard", params.cardName);
|
|
2736
|
+
if (!existing.success) return { success: false, message: `FlexCard '${params.cardName}' not found.` };
|
|
2737
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2738
|
+
const defMatch = existing.rawXml.match(/<omniUiCardDefinition[^>]*>([\s\S]*?)<\/omniUiCardDefinition>/i);
|
|
2739
|
+
const typeMatch = existing.rawXml.match(/<omniUiCardType[^>]*>([\s\S]*?)<\/omniUiCardType>/i);
|
|
2740
|
+
const xml = `<met:metadata xsi:type="met:OmniUiCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2741
|
+
<met:fullName>${x(params.cardName)}</met:fullName>
|
|
2742
|
+
<met:masterLabel>${labelMatch?.[1] ?? params.cardName}</met:masterLabel>
|
|
2743
|
+
<met:omniUiCardType>${typeMatch?.[1] ?? "Child"}</met:omniUiCardType>
|
|
2744
|
+
<met:isActive>true</met:isActive>
|
|
2745
|
+
${defMatch ? `<met:omniUiCardDefinition>${defMatch[1]}</met:omniUiCardDefinition>` : ""}
|
|
2746
|
+
</met:metadata>`;
|
|
2747
|
+
return await upsertMetadata(auth, xml);
|
|
2748
|
+
} catch (err) {
|
|
2749
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
export async function getFlexCard(auth, params) {
|
|
2753
|
+
try {
|
|
2754
|
+
const result = await readMetadataItem(auth, "OmniUiCard", params.cardName);
|
|
2755
|
+
if (!result.success) return result;
|
|
2756
|
+
const labelMatch = result.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2757
|
+
const isActiveMatch = result.rawXml.match(/<isActive[^>]*>([\s\S]*?)<\/isActive>/i);
|
|
2758
|
+
const defMatch = result.rawXml.match(/<omniUiCardDefinition[^>]*>([\s\S]*?)<\/omniUiCardDefinition>/i);
|
|
2759
|
+
let definition = null;
|
|
2760
|
+
if (defMatch) {
|
|
2761
|
+
try { definition = JSON.parse(defMatch[1].replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'")); }
|
|
2762
|
+
catch { definition = defMatch[1]; }
|
|
2763
|
+
}
|
|
2764
|
+
return { success: true, cardName: params.cardName, label: labelMatch?.[1] ?? "", isActive: isActiveMatch?.[1] === "true", definition, message: `FlexCard '${params.cardName}' retrieved.` };
|
|
2765
|
+
} catch (err) {
|
|
2766
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
export async function createOmniScript(auth, params) {
|
|
2770
|
+
try {
|
|
2771
|
+
const fullName = `${params.type}_${params.subType}_${params.language}`;
|
|
2772
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2773
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2774
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
2775
|
+
<met:type>${x(params.type)}</met:type>
|
|
2776
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2777
|
+
<met:language>${x(params.language)}</met:language>
|
|
2778
|
+
<met:omniProcessType>OmniScript</met:omniProcessType>
|
|
2779
|
+
<met:isActive>false</met:isActive>
|
|
2780
|
+
<met:isInternal>false</met:isInternal>
|
|
2781
|
+
<met:isLwcEnabled>${params.isLwcEnabled !== false ? "true" : "false"}</met:isLwcEnabled>
|
|
2782
|
+
<met:isOmniScriptEmbeddable>${params.isOmniScriptEmbeddable ? "true" : "false"}</met:isOmniScriptEmbeddable>
|
|
2783
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2784
|
+
</met:metadata>`;
|
|
2785
|
+
return await upsertMetadata(auth, xml);
|
|
2786
|
+
} catch (err) {
|
|
2787
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
export async function updateOmniScript(auth, params) {
|
|
2791
|
+
try {
|
|
2792
|
+
const fullName = `${params.type}_${params.subType}_${params.language}`;
|
|
2793
|
+
const existing = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2794
|
+
if (!existing.success) return { success: false, message: `OmniScript '${fullName}' not found.` };
|
|
2795
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2796
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2797
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2798
|
+
<met:masterLabel>${labelMatch?.[1] ?? fullName}</met:masterLabel>
|
|
2799
|
+
<met:type>${x(params.type)}</met:type>
|
|
2800
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2801
|
+
<met:language>${x(params.language)}</met:language>
|
|
2802
|
+
<met:omniProcessType>OmniScript</met:omniProcessType>
|
|
2803
|
+
<met:isActive>false</met:isActive>
|
|
2804
|
+
<met:isInternal>false</met:isInternal>
|
|
2805
|
+
${params.isLwcEnabled !== undefined ? `<met:isLwcEnabled>${params.isLwcEnabled}</met:isLwcEnabled>` : ""}
|
|
2806
|
+
${params.isOmniScriptEmbeddable !== undefined ? `<met:isOmniScriptEmbeddable>${params.isOmniScriptEmbeddable}</met:isOmniScriptEmbeddable>` : ""}
|
|
2807
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2808
|
+
</met:metadata>`;
|
|
2809
|
+
return await upsertMetadata(auth, xml);
|
|
2810
|
+
} catch (err) {
|
|
2811
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
export async function activateOmniScript(auth, params) {
|
|
2815
|
+
try {
|
|
2816
|
+
const fullName = `${params.type}_${params.subType}_${params.language}`;
|
|
2817
|
+
const existing = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2818
|
+
if (!existing.success) return { success: false, message: `OmniScript '${fullName}' not found.` };
|
|
2819
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2820
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2821
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2822
|
+
<met:masterLabel>${labelMatch?.[1] ?? fullName}</met:masterLabel>
|
|
2823
|
+
<met:type>${x(params.type)}</met:type>
|
|
2824
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2825
|
+
<met:language>${x(params.language)}</met:language>
|
|
2826
|
+
<met:omniProcessType>OmniScript</met:omniProcessType>
|
|
2827
|
+
<met:isActive>true</met:isActive>
|
|
2828
|
+
<met:isInternal>false</met:isInternal>
|
|
2829
|
+
<met:isLwcEnabled>true</met:isLwcEnabled>
|
|
2830
|
+
</met:metadata>`;
|
|
2831
|
+
return await upsertMetadata(auth, xml);
|
|
2832
|
+
} catch (err) {
|
|
2833
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
export async function getOmniScript(auth, params) {
|
|
2837
|
+
try {
|
|
2838
|
+
const fullName = `${params.type}_${params.subType}_${params.language}`;
|
|
2839
|
+
const result = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2840
|
+
if (!result.success) return result;
|
|
2841
|
+
const get = (tag) => result.rawXml.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, "i"))?.[1] ?? "";
|
|
2842
|
+
return {
|
|
2843
|
+
success: true,
|
|
2844
|
+
fullName,
|
|
2845
|
+
type: get("type"),
|
|
2846
|
+
subType: get("subType"),
|
|
2847
|
+
language: get("language"),
|
|
2848
|
+
isActive: get("isActive") === "true",
|
|
2849
|
+
isLwcEnabled: get("isLwcEnabled") === "true",
|
|
2850
|
+
message: `OmniScript '${fullName}' retrieved.`,
|
|
2851
|
+
};
|
|
2852
|
+
} catch (err) {
|
|
2853
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
export async function createDataRaptor(auth, params) {
|
|
2857
|
+
try {
|
|
2858
|
+
const mapItemsXml = (params.fields ?? []).map((f, i) => {
|
|
2859
|
+
const isJSON = f.sourceField.startsWith("$.");
|
|
2860
|
+
return `
|
|
2861
|
+
<met:mapItems>
|
|
2862
|
+
<met:index>${i}</met:index>
|
|
2863
|
+
<met:objectName>${x(params.objectApiName ?? "")}</met:objectName>
|
|
2864
|
+
<met:field>${x(isJSON ? "" : f.sourceField)}</met:field>
|
|
2865
|
+
<met:JSONPath>${x(isJSON ? f.sourceField : "")}</met:JSONPath>
|
|
2866
|
+
<met:targetField>${x(f.targetField)}</met:targetField>
|
|
2867
|
+
<met:dataType>${x(f.dataType ?? "String")}</met:dataType>
|
|
2868
|
+
${f.formula ? `<met:formula>${x(f.formula)}</met:formula>` : ""}
|
|
2869
|
+
<met:operationType>First</met:operationType>
|
|
2870
|
+
<met:sourceObjectName>${x(params.objectApiName ?? "")}</met:sourceObjectName>
|
|
2871
|
+
</met:mapItems>`;
|
|
2872
|
+
}).join("");
|
|
2873
|
+
const xml = `<met:metadata xsi:type="met:DataRaptorInterface" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2874
|
+
<met:fullName>${x(params.dataRaptorName)}</met:fullName>
|
|
2875
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
2876
|
+
<met:interfaceType>${x(params.interfaceType)}</met:interfaceType>
|
|
2877
|
+
${params.objectApiName ? `<met:objectName>${x(params.objectApiName)}</met:objectName>` : ""}
|
|
2878
|
+
${params.filterCriteria ? `<met:filterCriteria>${x(params.filterCriteria)}</met:filterCriteria>` : ""}
|
|
2879
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2880
|
+
${mapItemsXml}
|
|
2881
|
+
</met:metadata>`;
|
|
2882
|
+
return await upsertMetadata(auth, xml);
|
|
2883
|
+
} catch (err) {
|
|
2884
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
export async function getDataRaptor(auth, params) {
|
|
2888
|
+
try {
|
|
2889
|
+
const result = await readMetadataItem(auth, "DataRaptorInterface", params.dataRaptorName);
|
|
2890
|
+
if (!result.success) return result;
|
|
2891
|
+
const get = (tag) => result.rawXml.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, "i"))?.[1]?.trim() ?? "";
|
|
2892
|
+
const items = [...result.rawXml.matchAll(/<mapItems>([\s\S]*?)<\/mapItems>/gi)].map(m => {
|
|
2893
|
+
const inner = m[1];
|
|
2894
|
+
const gi = (t) => inner.match(new RegExp(`<${t}[^>]*>([\\s\\S]*?)<\\/${t}>`, "i"))?.[1]?.trim() ?? "";
|
|
2895
|
+
return { field: gi("field"), jsonPath: gi("JSONPath"), targetField: gi("targetField"), dataType: gi("dataType") };
|
|
2896
|
+
});
|
|
2897
|
+
return { success: true, dataRaptorName: params.dataRaptorName, label: get("masterLabel"), interfaceType: get("interfaceType"), objectName: get("objectName"), filterCriteria: get("filterCriteria"), fieldMappings: items, message: `DataRaptor '${params.dataRaptorName}' retrieved.` };
|
|
2898
|
+
} catch (err) {
|
|
2899
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
export async function createIntegrationProcedure(auth, params) {
|
|
2903
|
+
try {
|
|
2904
|
+
const fullName = `${params.procedureName}_${params.subType}`;
|
|
2905
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2906
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2907
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
2908
|
+
<met:type>${x(params.procedureName)}</met:type>
|
|
2909
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2910
|
+
<met:language>English</met:language>
|
|
2911
|
+
<met:omniProcessType>IntegrationProcedure</met:omniProcessType>
|
|
2912
|
+
<met:isActive>${params.isActive ? "true" : "false"}</met:isActive>
|
|
2913
|
+
<met:isInternal>false</met:isInternal>
|
|
2914
|
+
<met:isLwcEnabled>false</met:isLwcEnabled>
|
|
2915
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2916
|
+
</met:metadata>`;
|
|
2917
|
+
return await upsertMetadata(auth, xml);
|
|
2918
|
+
} catch (err) {
|
|
2919
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
export async function updateIntegrationProcedure(auth, params) {
|
|
2923
|
+
try {
|
|
2924
|
+
const fullName = `${params.procedureName}_${params.subType}`;
|
|
2925
|
+
const existing = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2926
|
+
if (!existing.success) return { success: false, message: `Integration Procedure '${fullName}' not found.` };
|
|
2927
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2928
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2929
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2930
|
+
<met:masterLabel>${labelMatch?.[1] ?? fullName}</met:masterLabel>
|
|
2931
|
+
<met:type>${x(params.procedureName)}</met:type>
|
|
2932
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2933
|
+
<met:language>English</met:language>
|
|
2934
|
+
<met:omniProcessType>IntegrationProcedure</met:omniProcessType>
|
|
2935
|
+
${params.isActive !== undefined ? `<met:isActive>${params.isActive}</met:isActive>` : "<met:isActive>false</met:isActive>"}
|
|
2936
|
+
<met:isInternal>false</met:isInternal>
|
|
2937
|
+
<met:isLwcEnabled>false</met:isLwcEnabled>
|
|
2938
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
2939
|
+
</met:metadata>`;
|
|
2940
|
+
return await upsertMetadata(auth, xml);
|
|
2941
|
+
} catch (err) {
|
|
2942
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
export async function activateIntegrationProcedure(auth, params) {
|
|
2946
|
+
try {
|
|
2947
|
+
const fullName = `${params.procedureName}_${params.subType}`;
|
|
2948
|
+
const existing = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2949
|
+
if (!existing.success) return { success: false, message: `Integration Procedure '${fullName}' not found.` };
|
|
2950
|
+
const labelMatch = existing.rawXml.match(/<masterLabel[^>]*>([\s\S]*?)<\/masterLabel>/i);
|
|
2951
|
+
const xml = `<met:metadata xsi:type="met:OmniScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
2952
|
+
<met:fullName>${x(fullName)}</met:fullName>
|
|
2953
|
+
<met:masterLabel>${labelMatch?.[1] ?? fullName}</met:masterLabel>
|
|
2954
|
+
<met:type>${x(params.procedureName)}</met:type>
|
|
2955
|
+
<met:subType>${x(params.subType)}</met:subType>
|
|
2956
|
+
<met:language>English</met:language>
|
|
2957
|
+
<met:omniProcessType>IntegrationProcedure</met:omniProcessType>
|
|
2958
|
+
<met:isActive>true</met:isActive>
|
|
2959
|
+
<met:isInternal>false</met:isInternal>
|
|
2960
|
+
<met:isLwcEnabled>false</met:isLwcEnabled>
|
|
2961
|
+
</met:metadata>`;
|
|
2962
|
+
return await upsertMetadata(auth, xml);
|
|
2963
|
+
} catch (err) {
|
|
2964
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
export async function getIntegrationProcedure(auth, params) {
|
|
2968
|
+
try {
|
|
2969
|
+
const fullName = `${params.procedureName}_${params.subType}`;
|
|
2970
|
+
const result = await readMetadataItem(auth, "OmniScript", fullName);
|
|
2971
|
+
if (!result.success) return result;
|
|
2972
|
+
const get = (tag) => result.rawXml.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, "i"))?.[1]?.trim() ?? "";
|
|
2973
|
+
return { success: true, fullName, type: get("type"), subType: get("subType"), isActive: get("isActive") === "true", description: get("description"), message: `Integration Procedure '${fullName}' retrieved.` };
|
|
2974
|
+
} catch (err) {
|
|
2975
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
export async function createCalculationMatrix(auth, params) {
|
|
2979
|
+
try {
|
|
2980
|
+
const inputVarsXml = params.inputVariables.map(v => `
|
|
2981
|
+
<met:inputVariables>
|
|
2982
|
+
<met:dataType>${x(v.dataType ?? "String")}</met:dataType>
|
|
2983
|
+
<met:name>${x(v.name)}</met:name>
|
|
2984
|
+
</met:inputVariables>`).join("");
|
|
2985
|
+
const outputVarsXml = params.outputVariables.map(v => `
|
|
2986
|
+
<met:outputVariables>
|
|
2987
|
+
<met:dataType>${x(v.dataType ?? "String")}</met:dataType>
|
|
2988
|
+
<met:name>${x(v.name)}</met:name>
|
|
2989
|
+
</met:outputVariables>`).join("");
|
|
2990
|
+
const rowsXml = (params.rows ?? []).map((row, ri) => {
|
|
2991
|
+
const cellsXml = Object.entries(row).map(([k, v]) => `
|
|
2992
|
+
<met:cells>
|
|
2993
|
+
<met:variableName>${x(k)}</met:variableName>
|
|
2994
|
+
<met:value>${x(v)}</met:value>
|
|
2995
|
+
</met:cells>`).join("");
|
|
2996
|
+
return `
|
|
2997
|
+
<met:matrixRows>
|
|
2998
|
+
<met:rowNumber>${ri + 1}</met:rowNumber>
|
|
2999
|
+
${cellsXml}
|
|
3000
|
+
</met:matrixRows>`;
|
|
3001
|
+
}).join("");
|
|
3002
|
+
const xml = `<met:metadata xsi:type="met:CalculationMatrix" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3003
|
+
<met:fullName>${x(params.matrixName)}</met:fullName>
|
|
3004
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
3005
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3006
|
+
${inputVarsXml}
|
|
3007
|
+
${outputVarsXml}
|
|
3008
|
+
${rowsXml}
|
|
3009
|
+
</met:metadata>`;
|
|
3010
|
+
return await upsertMetadata(auth, xml);
|
|
3011
|
+
} catch (err) {
|
|
3012
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
export async function createCalculationProcedure(auth, params) {
|
|
3016
|
+
try {
|
|
3017
|
+
const stepsXml = (params.steps ?? []).map((s, i) => `
|
|
3018
|
+
<met:calculationProcedureSteps>
|
|
3019
|
+
<met:stepNumber>${i + 1}</met:stepNumber>
|
|
3020
|
+
<met:developerName>${x(s.name)}</met:developerName>
|
|
3021
|
+
<met:stepType>${x(s.type ?? "Expression")}</met:stepType>
|
|
3022
|
+
${s.matrixName ? `<met:calculationMatrix>${x(s.matrixName)}</met:calculationMatrix>` : ""}
|
|
3023
|
+
${s.expression ? `<met:expression>${x(s.expression)}</met:expression>` : ""}
|
|
3024
|
+
${s.inputMap ? Object.entries(s.inputMap).map(([k,v]) => `<met:inputParameters><met:name>${x(k)}</met:name><met:value>${x(v)}</met:value></met:inputParameters>`).join("") : ""}
|
|
3025
|
+
${s.outputMap ? Object.entries(s.outputMap).map(([k,v]) => `<met:outputParameters><met:name>${x(k)}</met:name><met:value>${x(v)}</met:value></met:outputParameters>`).join("") : ""}
|
|
3026
|
+
</met:calculationProcedureSteps>`).join("");
|
|
3027
|
+
const xml = `<met:metadata xsi:type="met:CalculationProcedure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3028
|
+
<met:fullName>${x(params.procedureName)}</met:fullName>
|
|
3029
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
3030
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3031
|
+
${stepsXml}
|
|
3032
|
+
</met:metadata>`;
|
|
3033
|
+
return await upsertMetadata(auth, xml);
|
|
3034
|
+
} catch (err) {
|
|
3035
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
// ─── v2.3.0 OmniChannel Service Functions ────────────────────────────────────
|
|
3039
|
+
export async function createServiceChannel(auth, params) {
|
|
3040
|
+
try {
|
|
3041
|
+
const xml = `<met:metadata xsi:type="met:ServiceChannel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3042
|
+
<met:fullName>${x(params.channelName)}</met:fullName>
|
|
3043
|
+
<met:label>${x(params.label)}</met:label>
|
|
3044
|
+
<met:relatedEntity>${x(params.relatedObjectApiName ?? (params.channelType === "Case" ? "Case" : params.channelType === "Chat" ? "LiveChatTranscript" : params.channelType === "Email" ? "EmailMessage" : "Case"))}</met:relatedEntity>
|
|
3045
|
+
</met:metadata>`;
|
|
3046
|
+
return await upsertMetadata(auth, xml);
|
|
3047
|
+
} catch (err) {
|
|
3048
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
export async function createRoutingConfiguration(auth, params) {
|
|
3052
|
+
try {
|
|
3053
|
+
const xml = `<met:metadata xsi:type="met:RoutingConfiguration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3054
|
+
<met:fullName>${x(params.configName)}</met:fullName>
|
|
3055
|
+
<met:label>${x(params.label)}</met:label>
|
|
3056
|
+
<met:routingModel>${x(params.routingModel)}</met:routingModel>
|
|
3057
|
+
<met:routingPriority>${params.priority ?? 1}</met:routingPriority>
|
|
3058
|
+
${params.unitType === "Items" ? `<met:capacityWeight>${params.capacity ?? 1}</met:capacityWeight>` : `<met:capacityPercentage>${params.capacity ?? 100}</met:capacityPercentage>`}
|
|
3059
|
+
<met:unitType>${x(params.unitType ?? "Percentage")}</met:unitType>
|
|
3060
|
+
${params.pushTimeout !== undefined ? `<met:pushTimeout>${params.pushTimeout}</met:pushTimeout>` : ""}
|
|
3061
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3062
|
+
</met:metadata>`;
|
|
3063
|
+
return await upsertMetadata(auth, xml);
|
|
3064
|
+
} catch (err) {
|
|
3065
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
export async function createQueueRoutingConfig(auth, params) {
|
|
3069
|
+
try {
|
|
3070
|
+
// Link a routing config to a queue by updating the Queue metadata
|
|
3071
|
+
const readResult = await readMetadataItem(auth, "Queue", params.queueDeveloperName);
|
|
3072
|
+
if (!readResult.success) return { success: false, message: `Queue '${params.queueDeveloperName}' not found.` };
|
|
3073
|
+
const labelMatch = readResult.rawXml.match(/<label[^>]*>([\s\S]*?)<\/label>/i);
|
|
3074
|
+
const emailMatch = readResult.rawXml.match(/<email[^>]*>([\s\S]*?)<\/email>/i);
|
|
3075
|
+
const xml = `<met:metadata xsi:type="met:Queue" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3076
|
+
<met:fullName>${x(params.queueDeveloperName)}</met:fullName>
|
|
3077
|
+
<met:label>${labelMatch?.[1] ?? params.queueDeveloperName}</met:label>
|
|
3078
|
+
${emailMatch?.[1] ? `<met:email>${emailMatch[1]}</met:email>` : ""}
|
|
3079
|
+
<met:routingConfiguration>${x(params.routingConfigName)}</met:routingConfiguration>
|
|
3080
|
+
</met:metadata>`;
|
|
3081
|
+
return await upsertMetadata(auth, xml);
|
|
3082
|
+
} catch (err) {
|
|
3083
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
export async function createPresenceConfiguration(auth, params) {
|
|
3087
|
+
try {
|
|
3088
|
+
const channelsXml = (params.serviceChannels ?? []).map(ch => `
|
|
3089
|
+
<met:presenceConfigAssignments>
|
|
3090
|
+
<met:serviceChannel>${x(ch.channelName)}</met:serviceChannel>
|
|
3091
|
+
${ch.capacity ? `<met:capacity>${ch.capacity}</met:capacity>` : ""}
|
|
3092
|
+
</met:presenceConfigAssignments>`).join("");
|
|
3093
|
+
const xml = `<met:metadata xsi:type="met:PresenceUserConfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3094
|
+
<met:fullName>${x(params.configName)}</met:fullName>
|
|
3095
|
+
<met:label>${x(params.label)}</met:label>
|
|
3096
|
+
<met:capacity>${params.capacity ?? 10}</met:capacity>
|
|
3097
|
+
<met:allowAgentsToChangeSatus>${params.allowAgentsToChangeStatus !== false ? "true" : "false"}</met:allowAgentsToChangeSatus>
|
|
3098
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3099
|
+
${channelsXml}
|
|
3100
|
+
</met:metadata>`;
|
|
3101
|
+
return await upsertMetadata(auth, xml);
|
|
3102
|
+
} catch (err) {
|
|
3103
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
export async function createPresenceStatus(auth, params) {
|
|
3107
|
+
try {
|
|
3108
|
+
const channelsXml = (params.serviceChannels ?? []).map(ch => `
|
|
3109
|
+
<met:serviceChannels>
|
|
3110
|
+
<met:channel>${x(ch)}</met:channel>
|
|
3111
|
+
</met:serviceChannels>`).join("");
|
|
3112
|
+
const xml = `<met:metadata xsi:type="met:ServicePresenceStatus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3113
|
+
<met:fullName>${x(params.statusName)}</met:fullName>
|
|
3114
|
+
<met:label>${x(params.label)}</met:label>
|
|
3115
|
+
<met:statusType>${x(params.statusType ?? "Online")}</met:statusType>
|
|
3116
|
+
${channelsXml}
|
|
3117
|
+
</met:metadata>`;
|
|
3118
|
+
return await upsertMetadata(auth, xml);
|
|
3119
|
+
} catch (err) {
|
|
3120
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
export async function assignPresenceStatus(auth, params) {
|
|
3124
|
+
try {
|
|
3125
|
+
const results = [];
|
|
3126
|
+
for (const profileName of (params.profiles ?? [])) {
|
|
3127
|
+
const xml = `<met:metadata xsi:type="met:Profile" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3128
|
+
<met:fullName>${x(profileName)}</met:fullName>
|
|
3129
|
+
<met:servicePresenceStatusAccesses>
|
|
3130
|
+
<met:servicePresenceStatus>${x(params.statusName)}</met:servicePresenceStatus>
|
|
3131
|
+
<met:visible>true</met:visible>
|
|
3132
|
+
</met:servicePresenceStatusAccesses>
|
|
3133
|
+
</met:metadata>`;
|
|
3134
|
+
results.push(await upsertMetadata(auth, xml));
|
|
3135
|
+
}
|
|
3136
|
+
for (const psName of (params.permissionSets ?? [])) {
|
|
3137
|
+
const xml = `<met:metadata xsi:type="met:PermissionSet" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3138
|
+
<met:fullName>${x(psName)}</met:fullName>
|
|
3139
|
+
<met:servicePresenceStatusAccesses>
|
|
3140
|
+
<met:servicePresenceStatus>${x(params.statusName)}</met:servicePresenceStatus>
|
|
3141
|
+
<met:visible>true</met:visible>
|
|
3142
|
+
</met:servicePresenceStatusAccesses>
|
|
3143
|
+
</met:metadata>`;
|
|
3144
|
+
results.push(await upsertMetadata(auth, xml));
|
|
3145
|
+
}
|
|
3146
|
+
if (!results.length) return { success: false, message: "Provide at least one profile or permissionSet." };
|
|
3147
|
+
const allOk = results.every(r => r.success);
|
|
3148
|
+
return { success: allOk, message: allOk ? `Presence status '${params.statusName}' assigned to ${results.length} profile(s)/permission set(s).` : results.filter(r => !r.success).map(r => r.message).join("; ") };
|
|
3149
|
+
} catch (err) {
|
|
3150
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
export async function createSkill(auth, params) {
|
|
3154
|
+
try {
|
|
3155
|
+
// Skill is a REST SObject in Service Cloud
|
|
3156
|
+
const client = createClient(auth);
|
|
3157
|
+
const resp = await client.post("/sobjects/Skill", { MasterLabel: params.label, DeveloperName: params.skillName, Description: params.description ?? "" });
|
|
3158
|
+
return { success: true, fullName: resp.data.id, created: true, message: `Skill '${params.skillName}' created with ID ${resp.data.id}.` };
|
|
3159
|
+
} catch (err) {
|
|
3160
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
export async function assignSkillToAgent(auth, params) {
|
|
3164
|
+
try {
|
|
3165
|
+
const client = createClient(auth);
|
|
3166
|
+
// Get skill ID
|
|
3167
|
+
const skillResp = await client.get(`/query?q=${encodeURIComponent(`SELECT Id FROM Skill WHERE DeveloperName = '${params.skillName.replace(/'/g,"\\'")}' LIMIT 1`)}`);
|
|
3168
|
+
const skillId = skillResp.data.records?.[0]?.Id;
|
|
3169
|
+
if (!skillId) return { success: false, message: `Skill '${params.skillName}' not found.` };
|
|
3170
|
+
// Get user ID
|
|
3171
|
+
const userResp = await client.get(`/query?q=${encodeURIComponent(`SELECT Id FROM User WHERE Username = '${params.username.replace(/'/g,"\\'")}' LIMIT 1`)}`);
|
|
3172
|
+
const userId = userResp.data.records?.[0]?.Id;
|
|
3173
|
+
if (!userId) return { success: false, message: `User '${params.username}' not found.` };
|
|
3174
|
+
// Get or create ServiceResource
|
|
3175
|
+
const srResp = await client.get(`/query?q=${encodeURIComponent(`SELECT Id FROM ServiceResource WHERE RelatedRecordId = '${userId}' LIMIT 1`)}`);
|
|
3176
|
+
let srId = srResp.data.records?.[0]?.Id;
|
|
3177
|
+
if (!srId) {
|
|
3178
|
+
const newSr = await client.post("/sobjects/ServiceResource", { RelatedRecordId: userId, Name: params.username, ResourceType: "T", IsActive: true });
|
|
3179
|
+
srId = newSr.data.id;
|
|
3180
|
+
}
|
|
3181
|
+
// Create ServiceResourceSkill
|
|
3182
|
+
const resp = await client.post("/sobjects/ServiceResourceSkill", { ServiceResourceId: srId, SkillId: skillId, SkillLevel: params.skillLevel ?? 5, EffectiveStartDate: new Date().toISOString().slice(0, 10) });
|
|
3183
|
+
return { success: true, fullName: resp.data.id, created: true, message: `Skill '${params.skillName}' assigned to '${params.username}' with level ${params.skillLevel ?? 5}.` };
|
|
3184
|
+
} catch (err) {
|
|
3185
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
export async function createServiceTerritory(auth, params) {
|
|
3189
|
+
try {
|
|
3190
|
+
const client = createClient(auth);
|
|
3191
|
+
const payload = { Name: params.label, IsActive: params.isActive !== false };
|
|
3192
|
+
if (params.street) payload["Street"] = params.street;
|
|
3193
|
+
if (params.city) payload["City"] = params.city;
|
|
3194
|
+
if (params.state) payload["State"] = params.state;
|
|
3195
|
+
if (params.country) payload["Country"] = params.country;
|
|
3196
|
+
if (params.postalCode) payload["PostalCode"] = params.postalCode;
|
|
3197
|
+
if (params.operatingHoursName) {
|
|
3198
|
+
const ohResp = await client.get(`/query?q=${encodeURIComponent(`SELECT Id FROM OperatingHours WHERE Name = '${params.operatingHoursName.replace(/'/g,"\\'")}' LIMIT 1`)}`);
|
|
3199
|
+
const ohId = ohResp.data.records?.[0]?.Id;
|
|
3200
|
+
if (ohId) payload["OperatingHoursId"] = ohId;
|
|
3201
|
+
}
|
|
3202
|
+
const resp = await client.post("/sobjects/ServiceTerritory", payload);
|
|
3203
|
+
return { success: true, fullName: resp.data.id, created: true, message: `Service Territory '${params.label}' created with ID ${resp.data.id}.` };
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
export async function createWorkType(auth, params) {
|
|
3209
|
+
try {
|
|
3210
|
+
const client = createClient(auth);
|
|
3211
|
+
const durationInMinutes = params.durationType === "Hours" ? (params.estimatedDuration * 60) : params.estimatedDuration;
|
|
3212
|
+
const payload = {
|
|
3213
|
+
Name: params.label,
|
|
3214
|
+
EstimatedDuration: durationInMinutes,
|
|
3215
|
+
DurationType: "Minutes",
|
|
3216
|
+
BlockTimeBeforeWork: params.blockTimeBeforeWork ?? 0,
|
|
3217
|
+
BlockTimeAfterWork: params.blockTimeAfterWork ?? 0,
|
|
3218
|
+
Description: params.description ?? "",
|
|
3219
|
+
};
|
|
3220
|
+
const resp = await client.post("/sobjects/WorkType", payload);
|
|
3221
|
+
const workTypeId = resp.data.id;
|
|
3222
|
+
// Add skill requirements if provided
|
|
3223
|
+
for (const skillName of (params.skillRequirements ?? [])) {
|
|
3224
|
+
const skillResp = await client.get(`/query?q=${encodeURIComponent(`SELECT Id FROM Skill WHERE DeveloperName = '${skillName.replace(/'/g,"\\'")}' LIMIT 1`)}`);
|
|
3225
|
+
const skillId = skillResp.data.records?.[0]?.Id;
|
|
3226
|
+
if (skillId) await client.post("/sobjects/WorkTypeSkill", { WorkTypeId: workTypeId, SkillId: skillId, SkillType: "Required" }).catch(() => null);
|
|
3227
|
+
}
|
|
3228
|
+
return { success: true, fullName: workTypeId, created: true, message: `Work Type '${params.label}' created with ID ${workTypeId}.` };
|
|
3229
|
+
} catch (err) {
|
|
3230
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
export async function createMessagingChannel(auth, params) {
|
|
3234
|
+
try {
|
|
3235
|
+
const xml = `<met:metadata xsi:type="met:MessagingChannel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3236
|
+
<met:fullName>${x(params.channelName)}</met:fullName>
|
|
3237
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
3238
|
+
<met:channelType>${x(params.channelType)}</met:channelType>
|
|
3239
|
+
<met:isActive>true</met:isActive>
|
|
3240
|
+
${params.phoneNumber ? `<met:messagingPlatformKey>${x(params.phoneNumber)}</met:messagingPlatformKey>` : ""}
|
|
3241
|
+
${params.pageId ? `<met:pageId>${x(params.pageId)}</met:pageId>` : ""}
|
|
3242
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3243
|
+
</met:metadata>`;
|
|
3244
|
+
return await upsertMetadata(auth, xml);
|
|
3245
|
+
} catch (err) {
|
|
3246
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
export async function createChatButton(auth, params) {
|
|
3250
|
+
try {
|
|
3251
|
+
const xml = `<met:metadata xsi:type="met:LiveChatButton" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3252
|
+
<met:fullName>${x(params.buttonName)}</met:fullName>
|
|
3253
|
+
<met:label>${x(params.label)}</met:label>
|
|
3254
|
+
<met:type>Standard</met:type>
|
|
3255
|
+
<met:routingType>${params.routingType === "Bot" ? "Choice" : "Choice"}</met:routingType>
|
|
3256
|
+
<met:optionsHasTimeoutAlert>${params.optionsHasTimeoutAlert ? "true" : "false"}</met:optionsHasTimeoutAlert>
|
|
3257
|
+
<met:windowLanguage>${x(params.windowLanguage ?? "en")}</met:windowLanguage>
|
|
3258
|
+
${params.queueName ? `<met:overallQueueLength>0</met:overallQueueLength>` : ""}
|
|
3259
|
+
${params.customAgentName ? `<met:customAgentCrxName>${x(params.customAgentName)}</met:customAgentCrxName>` : ""}
|
|
3260
|
+
${params.inviteRenderer ? `<met:inviteRenderer>${x(params.inviteRenderer)}</met:inviteRenderer>` : ""}
|
|
3261
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3262
|
+
</met:metadata>`;
|
|
3263
|
+
return await upsertMetadata(auth, xml);
|
|
3264
|
+
} catch (err) {
|
|
3265
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
export async function createEmbeddedService(auth, params) {
|
|
3269
|
+
try {
|
|
3270
|
+
const isLiveAgent = params.channelType === "LiveAgent";
|
|
3271
|
+
const xml = `<met:metadata xsi:type="met:EmbeddedServiceConfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3272
|
+
<met:fullName>${x(params.deploymentName)}</met:fullName>
|
|
3273
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
3274
|
+
<met:embeddedServiceType>${isLiveAgent ? "Chat" : "Messaging"}</met:embeddedServiceType>
|
|
3275
|
+
<met:isEnabled>true</met:isEnabled>
|
|
3276
|
+
<met:site>${x(params.site)}</met:site>
|
|
3277
|
+
${isLiveAgent && params.chatButtonName ? `
|
|
3278
|
+
<met:embeddedServiceLiveAgent>
|
|
3279
|
+
<met:liveChatButton>${x(params.chatButtonName)}</met:liveChatButton>
|
|
3280
|
+
<met:embeddedServiceConfigName>${x(params.deploymentName)}</met:embeddedServiceConfigName>
|
|
3281
|
+
<met:offlineSupportEnabled>false</met:offlineSupportEnabled>
|
|
3282
|
+
</met:embeddedServiceLiveAgent>` : ""}
|
|
3283
|
+
${!isLiveAgent && params.messagingChannelName ? `<met:embeddedServiceMessaging><met:messagingChannel>${x(params.messagingChannelName)}</met:messagingChannel></met:embeddedServiceMessaging>` : ""}
|
|
3284
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3285
|
+
</met:metadata>`;
|
|
3286
|
+
return await upsertMetadata(auth, xml);
|
|
3287
|
+
} catch (err) {
|
|
3288
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
export async function createBotRouting(auth, params) {
|
|
3292
|
+
try {
|
|
3293
|
+
// Read the existing BotVersion to find the bot's ID
|
|
3294
|
+
const botVersions = await listMetadataType(auth, "BotVersion");
|
|
3295
|
+
if (!botVersions.success) return { success: false, message: "Could not list BotVersions." };
|
|
3296
|
+
const botFullName = botVersions.items.find(i => i.fullName.startsWith(params.botName + "."))?.fullName;
|
|
3297
|
+
if (!botFullName) return { success: false, message: `No BotVersion found for bot '${params.botName}'.` };
|
|
3298
|
+
const escalationsXml = (params.escalationConditions ?? [{ trigger: "agentRequested", action: "TransferToQueue" }]).map((ec, i) => `
|
|
3299
|
+
<met:botDialogs>
|
|
3300
|
+
<met:botSteps>
|
|
3301
|
+
<met:conversationStepType>Transfer</met:conversationStepType>
|
|
3302
|
+
<met:botMessage>${x(params.transferMessage ?? "Connecting you to an agent...")}</met:botMessage>
|
|
3303
|
+
<met:conversationTransfer>
|
|
3304
|
+
<met:transferToType>Queue</met:transferToType>
|
|
3305
|
+
<met:transferToName>${x(params.transferToQueueName)}</met:transferToName>
|
|
3306
|
+
</met:conversationTransfer>
|
|
3307
|
+
<met:stepIdentifier>step_${i + 1}</met:stepIdentifier>
|
|
3308
|
+
</met:botSteps>
|
|
3309
|
+
<met:developerName>Transfer_To_Agent_${i + 1}</met:developerName>
|
|
3310
|
+
<met:isGoalStep>false</met:isGoalStep>
|
|
3311
|
+
<met:label>Transfer To Agent ${i + 1}</met:label>
|
|
3312
|
+
<met:showInFooterMenu>false</met:showInFooterMenu>
|
|
3313
|
+
</met:botDialogs>`).join("");
|
|
3314
|
+
const xml = `<met:metadata xsi:type="met:BotVersion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3315
|
+
<met:fullName>${x(botFullName)}</met:fullName>
|
|
3316
|
+
${escalationsXml}
|
|
3317
|
+
</met:metadata>`;
|
|
3318
|
+
return await upsertMetadata(auth, xml);
|
|
3319
|
+
} catch (err) {
|
|
3320
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
// ─── v2.4.0 — NEW SERVICE FUNCTIONS ──────────────────────────────────────────
|
|
3324
|
+
export async function createLightningAppPage(auth, params) {
|
|
3325
|
+
return createFlexiPage(auth, { ...params, pageType: "AppPage" });
|
|
3326
|
+
}
|
|
3327
|
+
export async function deleteRecord(auth, params) {
|
|
3328
|
+
try {
|
|
3329
|
+
const client = createClient(auth);
|
|
3330
|
+
await client.del(`/sobjects/${params.objectApiName}/${params.recordId}`);
|
|
3331
|
+
return { success: true, fullName: params.recordId, created: false, message: `${params.objectApiName} record ${params.recordId} deleted successfully.` };
|
|
3332
|
+
} catch (err) {
|
|
3333
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
export async function getSetupAuditTrail(auth, params) {
|
|
3337
|
+
try {
|
|
3338
|
+
const client = createClient(auth);
|
|
3339
|
+
const conditions = [];
|
|
3340
|
+
conditions.push(`CreatedDate >= ${params.startDate}T00:00:00Z`);
|
|
3341
|
+
conditions.push(`CreatedDate <= ${params.endDate}T23:59:59Z`);
|
|
3342
|
+
if (params.createdByUsername) conditions.push(`CreatedBy.Username = '${params.createdByUsername.replace(/'/g, "\\'")}'`);
|
|
3343
|
+
if (params.section) conditions.push(`Section = '${params.section.replace(/'/g, "\\'")}'`);
|
|
3344
|
+
const soql = `SELECT Id, CreatedDate, CreatedBy.Username, Section, Action, Display FROM SetupAuditTrail WHERE ${conditions.join(" AND ")} ORDER BY CreatedDate DESC LIMIT ${params.limit ?? 100}`;
|
|
3345
|
+
const resp = await client.get(`/query?q=${encodeURIComponent(soql)}`);
|
|
3346
|
+
const records = (resp.data.records ?? []).map((r) => ({
|
|
3347
|
+
date: r.CreatedDate,
|
|
3348
|
+
username: r.CreatedBy?.Username ?? "",
|
|
3349
|
+
section: r.Section,
|
|
3350
|
+
action: r.Action,
|
|
3351
|
+
display: r.Display,
|
|
3352
|
+
}));
|
|
3353
|
+
return { success: true, totalSize: resp.data.totalSize, records, message: `${records.length} setup audit trail record(s) returned.` };
|
|
3354
|
+
} catch (err) {
|
|
3355
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
export async function getLoginHistory(auth, params) {
|
|
3359
|
+
try {
|
|
3360
|
+
const client = createClient(auth);
|
|
3361
|
+
const conditions = [];
|
|
3362
|
+
conditions.push(`LoginTime >= ${params.startDate}T00:00:00Z`);
|
|
3363
|
+
conditions.push(`LoginTime <= ${params.endDate}T23:59:59Z`);
|
|
3364
|
+
if (params.username) conditions.push(`Username = '${params.username.replace(/'/g, "\\'")}'`);
|
|
3365
|
+
if (params.status) conditions.push(`Status = '${params.status.replace(/'/g, "\\'")}'`);
|
|
3366
|
+
const soql = `SELECT Id, LoginTime, Username, SourceIp, Browser, Platform, Status, LoginType FROM LoginHistory WHERE ${conditions.join(" AND ")} ORDER BY LoginTime DESC LIMIT ${params.limit ?? 100}`;
|
|
3367
|
+
const resp = await client.get(`/query?q=${encodeURIComponent(soql)}`);
|
|
3368
|
+
const records = (resp.data.records ?? []).map((r) => ({
|
|
3369
|
+
loginTime: r.LoginTime,
|
|
3370
|
+
username: r.Username,
|
|
3371
|
+
sourceIp: r.SourceIp,
|
|
3372
|
+
browser: r.Browser,
|
|
3373
|
+
platform: r.Platform,
|
|
3374
|
+
status: r.Status,
|
|
3375
|
+
loginType: r.LoginType,
|
|
3376
|
+
}));
|
|
3377
|
+
return { success: true, totalSize: resp.data.totalSize, records, message: `${records.length} login history record(s) returned.` };
|
|
3378
|
+
} catch (err) {
|
|
3379
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
export async function getEventLogs(auth, params) {
|
|
3383
|
+
try {
|
|
3384
|
+
const client = createClient(auth);
|
|
3385
|
+
const soql = `SELECT Id, EventType, LogFile, LogDate, LogFileLength FROM EventLogFile WHERE EventType = '${params.eventType.replace(/'/g, "\\'")}' AND LogDate >= ${params.startDate}T00:00:00Z AND LogDate <= ${params.endDate}T23:59:59Z ORDER BY LogDate DESC LIMIT ${params.limit ?? 10}`;
|
|
3386
|
+
const resp = await client.get(`/query?q=${encodeURIComponent(soql)}`);
|
|
3387
|
+
const logFiles = resp.data.records ?? [];
|
|
3388
|
+
if (logFiles.length === 0) {
|
|
3389
|
+
return { success: true, totalSize: 0, logs: [], message: `No event log files found for type '${params.eventType}' in the specified date range.` };
|
|
3390
|
+
}
|
|
3391
|
+
const logs = [];
|
|
3392
|
+
for (const logFile of logFiles.slice(0, 3)) {
|
|
3393
|
+
if (!logFile.LogFile) continue;
|
|
3394
|
+
try {
|
|
3395
|
+
const csvResp = await fetchWithTimeout(`${auth.instanceUrl}${logFile.LogFile}`, {
|
|
3396
|
+
method: "GET",
|
|
3397
|
+
headers: { Authorization: `Bearer ${auth.accessToken}` },
|
|
3398
|
+
}, 30_000);
|
|
3399
|
+
if (csvResp.ok) {
|
|
3400
|
+
const csvText = await csvResp.text();
|
|
3401
|
+
const lines = csvText.split("\n").filter(l => l.trim());
|
|
3402
|
+
const headers = lines[0]?.split(",").map(h => h.replace(/"/g, "").trim()) ?? [];
|
|
3403
|
+
const rows = lines.slice(1, 21).map(line => {
|
|
3404
|
+
const values = line.split(",").map(v => v.replace(/"/g, "").trim());
|
|
3405
|
+
return Object.fromEntries(headers.map((h, i) => [h, values[i] ?? ""]));
|
|
3406
|
+
});
|
|
3407
|
+
logs.push({ logDate: logFile.LogDate, eventType: logFile.EventType, logFileLength: logFile.LogFileLength, entries: rows });
|
|
3408
|
+
}
|
|
3409
|
+
} catch {
|
|
3410
|
+
logs.push({ logDate: logFile.LogDate, eventType: logFile.EventType, logFileLength: logFile.LogFileLength, entries: [], note: "Could not fetch log content." });
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
return { success: true, totalSize: resp.data.totalSize, logs, message: `${logs.length} event log file(s) returned.` };
|
|
3414
|
+
} catch (err) {
|
|
3415
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
export async function getFieldHistory(auth, params) {
|
|
3419
|
+
try {
|
|
3420
|
+
const client = createClient(auth);
|
|
3421
|
+
const historyObject = `${params.objectApiName}History`;
|
|
3422
|
+
const fieldFilter = params.fields && params.fields.length > 0
|
|
3423
|
+
? ` AND Field IN (${params.fields.map(f => `'${f.replace(/'/g, "\\'")}'`).join(",")})`
|
|
3424
|
+
: "";
|
|
3425
|
+
const soql = `SELECT Id, Field, OldValue, NewValue, CreatedDate, CreatedBy.Username FROM ${historyObject} WHERE ParentId = '${params.recordId}'${fieldFilter} ORDER BY CreatedDate DESC LIMIT ${params.limit ?? 100}`;
|
|
3426
|
+
const resp = await client.get(`/query?q=${encodeURIComponent(soql)}`);
|
|
3427
|
+
const records = (resp.data.records ?? []).map((r) => ({
|
|
3428
|
+
date: r.CreatedDate,
|
|
3429
|
+
field: r.Field,
|
|
3430
|
+
oldValue: r.OldValue,
|
|
3431
|
+
newValue: r.NewValue,
|
|
3432
|
+
changedBy: r.CreatedBy?.Username ?? "",
|
|
3433
|
+
}));
|
|
3434
|
+
return { success: true, totalSize: resp.data.totalSize, records, message: `${records.length} field history record(s) returned.` };
|
|
3435
|
+
} catch (err) {
|
|
3436
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
export async function compareOrgs(params) {
|
|
3440
|
+
const { execSync: exec } = await import("child_process");
|
|
3441
|
+
function getAuthForAlias(alias) {
|
|
3442
|
+
if (!/^[A-Za-z0-9_-]+$/.test(alias)) throw new Error(`Invalid org alias: ${alias}`);
|
|
3443
|
+
let raw;
|
|
3444
|
+
try {
|
|
3445
|
+
raw = exec(`sf org display --target-org ${alias} --json`, { encoding: "utf-8", timeout: 30_000, env: { PATH: process.env["PATH"] ?? "" } });
|
|
3446
|
+
} catch (e) {
|
|
3447
|
+
throw new Error(`SF CLI failed for alias '${alias}': ${e instanceof Error ? sanitizeError(e.message) : "unknown error"}`);
|
|
3448
|
+
}
|
|
3449
|
+
const parsed = JSON.parse(raw);
|
|
3450
|
+
const accessToken = parsed?.result?.accessToken;
|
|
3451
|
+
const instanceUrl = parsed?.result?.instanceUrl;
|
|
3452
|
+
if (!accessToken || !instanceUrl) throw new Error(`No credentials returned for alias '${alias}'.`);
|
|
3453
|
+
return { instanceUrl: instanceUrl.replace(/\/$/, ""), accessToken };
|
|
3454
|
+
}
|
|
3455
|
+
try {
|
|
3456
|
+
const sourceAuth = getAuthForAlias(params.sourceAlias);
|
|
3457
|
+
const targetAuth = getAuthForAlias(params.targetAlias);
|
|
3458
|
+
const metadataTypes = params.metadataTypes ?? ["ApexClass","Flow","CustomObject","PermissionSet","LightningComponentBundle"];
|
|
3459
|
+
const results = { onlyInSource: [], onlyInTarget: [], differentInBoth: [], same: [] };
|
|
3460
|
+
for (const mdType of metadataTypes) {
|
|
3461
|
+
const [sourceList, targetList] = await Promise.all([
|
|
3462
|
+
listMetadataType(sourceAuth, mdType),
|
|
3463
|
+
listMetadataType(targetAuth, mdType),
|
|
3464
|
+
]);
|
|
3465
|
+
if (!sourceList.success || !targetList.success) continue;
|
|
3466
|
+
const sourceMap = new Map(sourceList.items.map(i => [i.fullName, i.lastModifiedDate]));
|
|
3467
|
+
const targetMap = new Map(targetList.items.map(i => [i.fullName, i.lastModifiedDate]));
|
|
3468
|
+
for (const [name, sourceDate] of sourceMap) {
|
|
3469
|
+
if (!targetMap.has(name)) {
|
|
3470
|
+
results.onlyInSource.push({ type: mdType, fullName: name, lastModified: sourceDate });
|
|
3471
|
+
} else {
|
|
3472
|
+
const targetDate = targetMap.get(name);
|
|
3473
|
+
if (sourceDate !== targetDate) {
|
|
3474
|
+
results.differentInBoth.push({ type: mdType, fullName: name, sourceLastModified: sourceDate, targetLastModified: targetDate });
|
|
3475
|
+
} else {
|
|
3476
|
+
results.same.push({ type: mdType, fullName: name });
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
for (const [name, targetDate] of targetMap) {
|
|
3481
|
+
if (!sourceMap.has(name)) {
|
|
3482
|
+
results.onlyInTarget.push({ type: mdType, fullName: name, lastModified: targetDate });
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
return { success: true, sourceAlias: params.sourceAlias, targetAlias: params.targetAlias,
|
|
3487
|
+
summary: { onlyInSource: results.onlyInSource.length, onlyInTarget: results.onlyInTarget.length, different: results.differentInBoth.length, same: results.same.length },
|
|
3488
|
+
onlyInSource: results.onlyInSource, onlyInTarget: results.onlyInTarget, differentInBoth: results.differentInBoth,
|
|
3489
|
+
message: `Comparison complete. Source-only: ${results.onlyInSource.length}, Target-only: ${results.onlyInTarget.length}, Different: ${results.differentInBoth.length}, Same: ${results.same.length}` };
|
|
3490
|
+
} catch (err) {
|
|
3491
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
export async function createCertificate(auth, params) {
|
|
3495
|
+
try {
|
|
3496
|
+
const xml = `<met:metadata xsi:type="met:Certificate" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3497
|
+
<met:fullName>${x(params.certName)}</met:fullName>
|
|
3498
|
+
<met:masterLabel>${x(params.label)}</met:masterLabel>
|
|
3499
|
+
<met:keySize>${x(params.keySize ?? "2048")}</met:keySize>
|
|
3500
|
+
<met:privateKeyExportable>${params.privateKeyExportable ? "true" : "false"}</met:privateKeyExportable>
|
|
3501
|
+
${params.expirationDate ? `<met:expirationDate>${x(params.expirationDate)}</met:expirationDate>` : ""}
|
|
3502
|
+
</met:metadata>`;
|
|
3503
|
+
return await upsertMetadata(auth, xml);
|
|
3504
|
+
} catch (err) {
|
|
3505
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
export async function createEventRelay(auth, params) {
|
|
3509
|
+
try {
|
|
3510
|
+
const xml = `<met:metadata xsi:type="met:EventRelayConfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3511
|
+
<met:fullName>${x(params.relayName)}</met:fullName>
|
|
3512
|
+
<met:label>${x(params.label)}</met:label>
|
|
3513
|
+
<met:eventChannel>${x(params.eventChannel)}</met:eventChannel>
|
|
3514
|
+
<met:destinationType>${x(params.destinationType)}</met:destinationType>
|
|
3515
|
+
<met:destinationResourceName>${x(params.destinationResourceName)}</met:destinationResourceName>
|
|
3516
|
+
<met:state>${x(params.state ?? "RUN")}</met:state>
|
|
3517
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3518
|
+
</met:metadata>`;
|
|
3519
|
+
return await upsertMetadata(auth, xml);
|
|
3520
|
+
} catch (err) {
|
|
3521
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
export async function createLetterhead(auth, params) {
|
|
3525
|
+
try {
|
|
3526
|
+
const xml = `<met:metadata xsi:type="met:Letterhead" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3527
|
+
<met:fullName>${x(params.letterheadName)}</met:fullName>
|
|
3528
|
+
<met:available>true</met:available>
|
|
3529
|
+
<met:backgroundColor>${x(params.bodyColor ?? "#FFFFFF")}</met:backgroundColor>
|
|
3530
|
+
<met:bodyColor>${x(params.bodyColor ?? "#FFFFFF")}</met:bodyColor>
|
|
3531
|
+
<met:description>${x(params.footerText ?? "")}</met:description>
|
|
3532
|
+
<met:footer>
|
|
3533
|
+
<met:backgroundColor>${x(params.footerColor ?? "#404040")}</met:backgroundColor>
|
|
3534
|
+
<met:color>${x(params.footerFontColor ?? "#FFFFFF")}</met:color>
|
|
3535
|
+
<met:fontFace>${x(params.fontName ?? "Arial")}</met:fontFace>
|
|
3536
|
+
<met:fontSize>${params.fontSize ?? 12}</met:fontSize>
|
|
3537
|
+
<met:height>60</met:height>
|
|
3538
|
+
<met:horizontalAlignment>Center</met:horizontalAlignment>
|
|
3539
|
+
<met:verticalAlignment>Bottom</met:verticalAlignment>
|
|
3540
|
+
</met:footer>
|
|
3541
|
+
<met:header>
|
|
3542
|
+
<met:backgroundColor>${x(params.headerColor ?? "#0070D2")}</met:backgroundColor>
|
|
3543
|
+
<met:color>${x(params.headerFontColor ?? "#FFFFFF")}</met:color>
|
|
3544
|
+
<met:fontFace>${x(params.fontName ?? "Arial")}</met:fontFace>
|
|
3545
|
+
<met:fontSize>${params.fontSize ?? 12}</met:fontSize>
|
|
3546
|
+
<met:height>60</met:height>
|
|
3547
|
+
<met:horizontalAlignment>Center</met:horizontalAlignment>
|
|
3548
|
+
<met:verticalAlignment>Middle</met:verticalAlignment>
|
|
3549
|
+
</met:header>
|
|
3550
|
+
<met:name>${x(params.label)}</met:name>
|
|
3551
|
+
<met:topLine>
|
|
3552
|
+
<met:color>${x(params.topLineColor ?? "#0070D2")}</met:color>
|
|
3553
|
+
<met:height>3</met:height>
|
|
3554
|
+
<met:style>Solid</met:style>
|
|
3555
|
+
</met:topLine>
|
|
3556
|
+
<met:bottomLine>
|
|
3557
|
+
<met:color>${x(params.bottomLineColor ?? "#0070D2")}</met:color>
|
|
3558
|
+
<met:height>3</met:height>
|
|
3559
|
+
<met:style>Solid</met:style>
|
|
3560
|
+
</met:bottomLine>
|
|
3561
|
+
</met:metadata>`;
|
|
3562
|
+
return await upsertMetadata(auth, xml);
|
|
3563
|
+
} catch (err) {
|
|
3564
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
export async function sendEmail(auth, params) {
|
|
3568
|
+
try {
|
|
3569
|
+
const client = createClient(auth);
|
|
3570
|
+
const emailBody = params.htmlBody || params.body || "";
|
|
3571
|
+
const payload = {
|
|
3572
|
+
inputs: [{
|
|
3573
|
+
emailAddresses: params.toAddresses.join(","),
|
|
3574
|
+
emailSubject: params.subject,
|
|
3575
|
+
emailBody,
|
|
3576
|
+
saveAsActivity: params.saveAsActivity ?? true,
|
|
3577
|
+
senderAddress: "",
|
|
3578
|
+
...(params.whoId && { targetObjectId: params.whoId }),
|
|
3579
|
+
...(params.whatId && { whatId: params.whatId }),
|
|
3580
|
+
...(params.ccAddresses?.length && { ccAddresses: params.ccAddresses.join(",") }),
|
|
3581
|
+
}],
|
|
3582
|
+
};
|
|
3583
|
+
const resp = await client.post("/actions/standard/emailSimple", payload);
|
|
3584
|
+
const output = Array.isArray(resp.data) ? resp.data[0] : resp.data;
|
|
3585
|
+
if (output?.isSuccess === false) {
|
|
3586
|
+
const errMsg = output?.errors?.map((e) => e.message).join("; ") ?? "Email action failed.";
|
|
3587
|
+
return { success: false, message: errMsg };
|
|
3588
|
+
}
|
|
3589
|
+
return { success: true, fullName: "sendEmail", created: false, message: `Email sent to ${params.toAddresses.join(", ")}.` };
|
|
3590
|
+
} catch (err) {
|
|
3591
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
export async function createEinsteinPrediction(auth, params) {
|
|
3595
|
+
try {
|
|
3596
|
+
const xml = `<met:metadata xsi:type="met:MLPredictionDefinition" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3597
|
+
<met:fullName>${x(params.predictionName)}</met:fullName>
|
|
3598
|
+
<met:label>${x(params.label)}</met:label>
|
|
3599
|
+
<met:active>false</met:active>
|
|
3600
|
+
<met:negativeLabel>${x(params.negativeLabel ?? "No")}</met:negativeLabel>
|
|
3601
|
+
<met:positiveLabel>${x(params.positiveLabel ?? "Yes")}</met:positiveLabel>
|
|
3602
|
+
<met:predictionType>${x(params.predictionType)}</met:predictionType>
|
|
3603
|
+
<met:status>Draft</met:status>
|
|
3604
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3605
|
+
${params.pushbackField ? `<met:pushbackField>
|
|
3606
|
+
<met:fieldName>${x(params.pushbackField)}</met:fieldName>
|
|
3607
|
+
<met:objectName>${x(params.objectApiName)}</met:objectName>
|
|
3608
|
+
</met:pushbackField>` : ""}
|
|
3609
|
+
</met:metadata>`;
|
|
3610
|
+
return await upsertMetadata(auth, xml);
|
|
3611
|
+
} catch (err) {
|
|
3612
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
export async function createNextBestAction(auth, params) {
|
|
3616
|
+
try {
|
|
3617
|
+
const recsXml = (params.recommendations ?? []).map(rec => `
|
|
3618
|
+
<met:recommendationDefinitions>
|
|
3619
|
+
<met:developerName>${x(rec.name)}</met:developerName>
|
|
3620
|
+
<met:label>${x(rec.label)}</met:label>
|
|
3621
|
+
<met:acceptanceLabel>${x(rec.acceptanceLabel ?? "Accept")}</met:acceptanceLabel>
|
|
3622
|
+
<met:rejectionLabel>${x(rec.rejectionLabel ?? "Decline")}</met:rejectionLabel>
|
|
3623
|
+
${rec.actionReference ? `<met:actionReference>${x(rec.actionReference)}</met:actionReference>` : ""}
|
|
3624
|
+
</met:recommendationDefinitions>`).join("");
|
|
3625
|
+
const xml = `<met:metadata xsi:type="met:RecommendationStrategy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3626
|
+
<met:fullName>${x(params.strategyName)}</met:fullName>
|
|
3627
|
+
<met:label>${x(params.label)}</met:label>
|
|
3628
|
+
<met:contextObjectName>${x(params.contextObjectApiName)}</met:contextObjectName>
|
|
3629
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3630
|
+
${recsXml}
|
|
3631
|
+
</met:metadata>`;
|
|
3632
|
+
return await upsertMetadata(auth, xml);
|
|
3633
|
+
} catch (err) {
|
|
3634
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
export async function createEinsteinBot(auth, params) {
|
|
3638
|
+
try {
|
|
3639
|
+
const mlIntentsXml = (params.dialogs ?? []).map(d => `
|
|
3640
|
+
<met:mlIntents>
|
|
3641
|
+
<met:developerName>${x(d.name)}</met:developerName>
|
|
3642
|
+
<met:label>${x(d.label)}</met:label>
|
|
3643
|
+
${(d.utterances ?? []).map(u => `<met:mlIntentUtterances><met:utterance>${x(u)}</met:utterance></met:mlIntentUtterances>`).join("")}
|
|
3644
|
+
</met:mlIntents>`).join("");
|
|
3645
|
+
const dialogsXml = (params.dialogs ?? [{ name: "Welcome", label: "Welcome", type: "Main", isGoalStep: false, messages: ["Hello! How can I help you today?"] }]).map(d => `
|
|
3646
|
+
<met:botDialogs>
|
|
3647
|
+
${(d.messages ?? ["Hello!"]).map((msg, i) => `
|
|
3648
|
+
<met:botSteps>
|
|
3649
|
+
<met:conversationStepType>Message</met:conversationStepType>
|
|
3650
|
+
<met:botMessage>${x(msg)}</met:botMessage>
|
|
3651
|
+
<met:stepIdentifier>${x(d.name)}_step${i + 1}</met:stepIdentifier>
|
|
3652
|
+
</met:botSteps>`).join("")}
|
|
3653
|
+
<met:developerName>${x(d.name)}</met:developerName>
|
|
3654
|
+
<met:isGoalStep>${d.isGoalStep ? "true" : "false"}</met:isGoalStep>
|
|
3655
|
+
<met:label>${x(d.label)}</met:label>
|
|
3656
|
+
<met:showInFooterMenu>false</met:showInFooterMenu>
|
|
3657
|
+
</met:botDialogs>`).join("");
|
|
3658
|
+
const botXml = `<met:metadata xsi:type="met:Bot" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3659
|
+
<met:fullName>${x(params.botName)}</met:fullName>
|
|
3660
|
+
<met:label>${x(params.label)}</met:label>
|
|
3661
|
+
<met:defaultLocale>${x(params.defaultLocale ?? "en_US")}</met:defaultLocale>
|
|
3662
|
+
${params.description ? `<met:description>${x(params.description)}</met:description>` : ""}
|
|
3663
|
+
<met:mlDomain>
|
|
3664
|
+
<met:developerName>${x(params.botName)}_MLDomain</met:developerName>
|
|
3665
|
+
<met:label>${x(params.label)} ML Domain</met:label>
|
|
3666
|
+
${mlIntentsXml}
|
|
3667
|
+
</met:mlDomain>
|
|
3668
|
+
</met:metadata>`;
|
|
3669
|
+
const botResult = await upsertMetadata(auth, botXml);
|
|
3670
|
+
if (!botResult.success) return botResult;
|
|
3671
|
+
const versionXml = `<met:metadata xsi:type="met:BotVersion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
3672
|
+
<met:fullName>${x(params.botName)}.v1</met:fullName>
|
|
3673
|
+
${dialogsXml}
|
|
3674
|
+
</met:metadata>`;
|
|
3675
|
+
const versionResult = await upsertMetadata(auth, versionXml);
|
|
3676
|
+
return { success: versionResult.success, fullName: params.botName, created: true, message: versionResult.success ? `Einstein Bot '${params.botName}' created with ${(params.dialogs ?? []).length || 1} dialog(s).` : versionResult.message };
|
|
3677
|
+
} catch (err) {
|
|
3678
|
+
return { success: false, message: sanitizeError(err instanceof Error ? err.message : String(err)) };
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
2656
3681
|
//# sourceMappingURL=salesforce.js.map
|