@tailor-platform/sdk 1.32.0 → 1.33.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 +61 -0
- package/dist/{application-B8vok3w2.mjs → application-CfAom-vi.mjs} +38 -29
- package/dist/application-CfAom-vi.mjs.map +1 -0
- package/dist/application-ChVyhwe-.mjs +12 -0
- package/dist/{brand-GZnI4eYb.mjs → brand-0SscafcY.mjs} +2 -1
- package/dist/{brand-GZnI4eYb.mjs.map → brand-0SscafcY.mjs.map} +1 -1
- package/dist/{chunk-DEt8GZDa.mjs → chunk-COzJYswC.mjs} +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +30 -29
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +10 -6
- package/dist/cli/lib.mjs +14 -13
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/skills.d.mts +1 -0
- package/dist/cli/skills.mjs +2 -1
- package/dist/cli/skills.mjs.map +1 -1
- package/dist/{client-DfdgRZlQ.mjs → client-D5P1myz0.mjs} +6 -5
- package/dist/{client-DfdgRZlQ.mjs.map → client-D5P1myz0.mjs.map} +1 -1
- package/dist/{client-B0wrLUVK.mjs → client-gpRgZl1b.mjs} +5 -4
- package/dist/configure/index.d.mts +6 -5
- package/dist/configure/index.mjs +85 -14
- package/dist/configure/index.mjs.map +1 -1
- package/dist/crash-report-Cm9vFmTN.mjs +7 -0
- package/dist/{crash-report-wNxS0IUJ.mjs → crash-report-DnwITWeV.mjs} +5 -4
- package/dist/{crash-report-wNxS0IUJ.mjs.map → crash-report-DnwITWeV.mjs.map} +1 -1
- package/dist/{enum-constants-D1nfn0qD.mjs → enum-constants-Piv_E-2M.mjs} +3 -1
- package/dist/{enum-constants-D1nfn0qD.mjs.map → enum-constants-Piv_E-2M.mjs.map} +1 -1
- package/dist/{env-BgdHfWDn.d.mts → env-CSZ9CKg7.d.mts} +3 -2
- package/dist/{file-utils-Bctuzn3x.mjs → file-utils-B7xME5IK.mjs} +3 -1
- package/dist/{file-utils-Bctuzn3x.mjs.map → file-utils-B7xME5IK.mjs.map} +1 -1
- package/dist/{index-BAud4CE7.d.mts → index-BdlrrjvD.d.mts} +145 -35
- package/dist/{index-D4Y81vh1.d.mts → index-Dlpe_4Nd.d.mts} +4 -2
- package/dist/{index-FZMBoUWm.d.mts → index-IHl7P_9I.d.mts} +4 -2
- package/dist/{index-CxgBnxKM.d.mts → index-dg3Sf-No.d.mts} +4 -2
- package/dist/{index-CdDzh-T2.d.mts → index-uNv9YJgx.d.mts} +4 -2
- package/dist/{interceptor-DgQNmwWJ.mjs → interceptor-1LL1gBsF.mjs} +3 -2
- package/dist/{interceptor-DgQNmwWJ.mjs.map → interceptor-1LL1gBsF.mjs.map} +1 -1
- package/dist/{job-DdfW7vH3.mjs → job-CPKYCk_e.mjs} +3 -2
- package/dist/{job-DdfW7vH3.mjs.map → job-CPKYCk_e.mjs.map} +1 -1
- package/dist/kysely/index.d.mts +1 -0
- package/dist/kysely/index.mjs +2 -1
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/{kysely-type-B_IecdK9.mjs → kysely-type-t5MbP7iJ.mjs} +3 -1
- package/dist/{kysely-type-B_IecdK9.mjs.map → kysely-type-t5MbP7iJ.mjs.map} +1 -1
- package/dist/{logger-CqezTedh.mjs → logger-qz-Y4sBV.mjs} +2 -1
- package/dist/{logger-CqezTedh.mjs.map → logger-qz-Y4sBV.mjs.map} +1 -1
- package/dist/package-json-88x7bPKC.mjs +5 -0
- package/dist/{package-json-D3x2nBPB.mjs → package-json-VqyFvGiP.mjs} +2 -1
- package/dist/{package-json-D3x2nBPB.mjs.map → package-json-VqyFvGiP.mjs.map} +1 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +2 -1
- package/dist/plugin/builtin/enum-constants/index.mjs +3 -2
- package/dist/plugin/builtin/file-utils/index.d.mts +2 -1
- package/dist/plugin/builtin/file-utils/index.mjs +3 -2
- package/dist/plugin/builtin/kysely-type/index.d.mts +2 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +3 -2
- package/dist/plugin/builtin/seed/index.d.mts +2 -1
- package/dist/plugin/builtin/seed/index.mjs +3 -2
- package/dist/plugin/index.d.mts +3 -2
- package/dist/plugin/index.mjs +2 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/{plugin-DyVYXZWz.d.mts → plugin-BC7WQrjm.d.mts} +25 -13
- package/dist/{runtime-DbX_UxlC.mjs → runtime-3P9JFpe9.mjs} +1036 -321
- package/dist/runtime-3P9JFpe9.mjs.map +1 -0
- package/dist/{schema-CNWt2FKQ.mjs → schema-D27cW0Ca.mjs} +4 -2
- package/dist/{schema-CNWt2FKQ.mjs.map → schema-D27cW0Ca.mjs.map} +1 -1
- package/dist/seed/index.d.mts +1 -0
- package/dist/seed/index.mjs +2 -1
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{seed-CWkIDWMb.mjs → seed-DtYgudLq.mjs} +12 -3
- package/dist/seed-DtYgudLq.mjs.map +1 -0
- package/dist/{telemetry-BSUlYTs-.mjs → telemetry-B4sp-dD2.mjs} +3 -2
- package/dist/{telemetry-BSUlYTs-.mjs.map → telemetry-B4sp-dD2.mjs.map} +1 -1
- package/dist/telemetry-DXIGGa6A.mjs +5 -0
- package/dist/utils/test/index.d.mts +3 -2
- package/dist/utils/test/index.mjs +4 -3
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/{workflow.generated-DMQ-cjyT.d.mts → workflow.generated-Cd5dsFnf.d.mts} +3 -2
- package/docs/cli/application.md +17 -0
- package/docs/generator/builtin.md +0 -8
- package/docs/services/executor.md +54 -0
- package/package.json +18 -17
- package/dist/application-B8vok3w2.mjs.map +0 -1
- package/dist/application-DHifc-oA.mjs +0 -11
- package/dist/crash-report-BMEhRxeg.mjs +0 -6
- package/dist/package-json-DiZWrkIA.mjs +0 -4
- package/dist/runtime-DbX_UxlC.mjs.map +0 -1
- package/dist/seed-CWkIDWMb.mjs.map +0 -1
- package/dist/telemetry-BtN2l0f1.mjs +0 -4
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { r as
|
|
1
|
+
|
|
2
|
+
import { t as db } from "./schema-D27cW0Ca.mjs";
|
|
3
|
+
import { i as symbols, n as logger, r as styles, t as CIPromptError } from "./logger-qz-Y4sBV.mjs";
|
|
4
|
+
import { A as ExecutorTriggerType, C as TailorDBType_PermitAction, E as FunctionExecution_Status, F as AuthOAuth2Client_GrantType, G as Condition_Operator, H as UserProfileProviderConfig_UserProfileProviderType, I as AuthSCIMAttribute_Mutability, J as ApplicationSchemaUpdateAttemptStatus, K as FilterSchema, L as AuthSCIMAttribute_Type, M as AuthIDPConfig_AuthType, N as AuthInvokerSchema, O as ExecutorJobStatus, P as AuthOAuth2Client_ClientType, R as AuthSCIMAttribute_Uniqueness, S as TailorDBType_Permission_Permit, T as IdPLang, U as GetApplicationSchemaHealthResponse_ApplicationSchemaHealthStatus, V as TenantProviderConfig_TenantProviderType, W as ConditionSchema, Y as Subgraph_ServiceType, _ as WorkflowJobExecution_Status, a as fetchMachineUserToken, b as TailorDBGQLPermission_Permit, f as platformBaseUrl, g as WorkflowExecution_Status, h as WorkspacePlatformUserRole, i as fetchAll, j as AuthHookPoint, k as ExecutorTargetType, m as userAgent, p as resolveStaticWebsiteUrls, q as PageDirection, u as initOperatorClient, v as TailorDBGQLPermission_Action, w as PipelineResolver_OperationType, x as TailorDBType_Permission_Operator, y as TailorDBGQLPermission_Operator, z as AuthSCIMConfig_AuthorizationType } from "./client-D5P1myz0.mjs";
|
|
5
|
+
import { t as readPackageJson } from "./package-json-VqyFvGiP.mjs";
|
|
6
|
+
import { S as readPlatformConfig, T as writePlatformConfig, _ as hashFile, a as loadConfig, b as loadAccessToken, d as TailorDBTypeSchema, f as stringifyFunction, g as getDistDir, h as createBundleCache, l as OAuth2ClientSchema, m as loadFilesWithIgnores, n as generatePluginFilesIfNeeded, p as tailorUserMap, r as loadApplication, s as createExecutorService, t as defineApplication, x as loadWorkspaceId } from "./application-CfAom-vi.mjs";
|
|
7
|
+
import { r as withSpan } from "./telemetry-B4sp-dD2.mjs";
|
|
7
8
|
import { arg, createDefineCommand, defineCommand, runCommand } from "politty";
|
|
8
9
|
import { z } from "zod";
|
|
9
10
|
import * as fs$1 from "node:fs";
|
|
@@ -890,6 +891,7 @@ function createChangeSet(title) {
|
|
|
890
891
|
const updates = [];
|
|
891
892
|
const deletes = [];
|
|
892
893
|
const replaces = [];
|
|
894
|
+
const unchanged = [];
|
|
893
895
|
const isEmpty = () => creates.length === 0 && updates.length === 0 && deletes.length === 0 && replaces.length === 0;
|
|
894
896
|
return {
|
|
895
897
|
title,
|
|
@@ -897,6 +899,7 @@ function createChangeSet(title) {
|
|
|
897
899
|
updates,
|
|
898
900
|
deletes,
|
|
899
901
|
replaces,
|
|
902
|
+
unchanged,
|
|
900
903
|
isEmpty,
|
|
901
904
|
print: () => {
|
|
902
905
|
if (isEmpty()) return;
|
|
@@ -908,6 +911,83 @@ function createChangeSet(title) {
|
|
|
908
911
|
}
|
|
909
912
|
};
|
|
910
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* Summarize resource counts across multiple change sets.
|
|
916
|
+
* @param changeSets - Change sets to aggregate
|
|
917
|
+
* @returns Aggregated plan counts by action
|
|
918
|
+
*/
|
|
919
|
+
function summarizeChangeSets(changeSets) {
|
|
920
|
+
const summary = {
|
|
921
|
+
create: 0,
|
|
922
|
+
update: 0,
|
|
923
|
+
delete: 0,
|
|
924
|
+
replace: 0,
|
|
925
|
+
unchanged: 0
|
|
926
|
+
};
|
|
927
|
+
for (const changeSet of changeSets) {
|
|
928
|
+
summary.create += changeSet.creates.length;
|
|
929
|
+
summary.update += changeSet.updates.length;
|
|
930
|
+
summary.delete += changeSet.deletes.length;
|
|
931
|
+
summary.replace += changeSet.replaces.length;
|
|
932
|
+
summary.unchanged += changeSet.unchanged.length;
|
|
933
|
+
}
|
|
934
|
+
return summary;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Format an aggregated plan summary for CLI output.
|
|
938
|
+
* @param summary - Aggregated plan counts
|
|
939
|
+
* @returns Human-readable plan summary line
|
|
940
|
+
*/
|
|
941
|
+
function formatPlanSummary(summary) {
|
|
942
|
+
const parts = [
|
|
943
|
+
`${summary.create} to create`,
|
|
944
|
+
`${summary.update} to update`,
|
|
945
|
+
`${summary.delete} to delete`
|
|
946
|
+
];
|
|
947
|
+
if (summary.replace > 0) parts.push(`${summary.replace} to replace`);
|
|
948
|
+
parts.push(`${summary.unchanged} unchanged`);
|
|
949
|
+
return `Plan: ${parts.join(", ")}`;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
//#endregion
|
|
953
|
+
//#region src/cli/commands/apply/compare.ts
|
|
954
|
+
/**
|
|
955
|
+
* Stable JSON-like serialization that sorts object keys and ignores proto runtime metadata.
|
|
956
|
+
* @param value - Value to serialize
|
|
957
|
+
* @returns Stable serialized string
|
|
958
|
+
*/
|
|
959
|
+
function stableStringify(value) {
|
|
960
|
+
if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : stableStringify(item)).join(",")}]`;
|
|
961
|
+
if (value && typeof value === "object") return `{${Object.entries(value).filter(([key, entryValue]) => key !== "$typeName" && entryValue !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`).join(",")}}`;
|
|
962
|
+
if (typeof value === "bigint") return JSON.stringify(value.toString());
|
|
963
|
+
return JSON.stringify(value);
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Normalize a proto-ish object into a plain JSON-compatible structure for comparison.
|
|
967
|
+
* @param value - Value to normalize
|
|
968
|
+
* @returns Normalized value
|
|
969
|
+
*/
|
|
970
|
+
function normalizeProtoConfig(value) {
|
|
971
|
+
if (value === void 0 || value === null) return value;
|
|
972
|
+
return JSON.parse(stableStringify(value));
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Sort a string array for order-insensitive comparison.
|
|
976
|
+
* @param values - Values to sort
|
|
977
|
+
* @returns Sorted values
|
|
978
|
+
*/
|
|
979
|
+
function normalizeStringArray(values) {
|
|
980
|
+
return [...values ?? []].sort();
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Compare two values after proto normalization.
|
|
984
|
+
* @param left - Left value
|
|
985
|
+
* @param right - Right value
|
|
986
|
+
* @returns True when normalized values are equal
|
|
987
|
+
*/
|
|
988
|
+
function areNormalizedEqual(left, right) {
|
|
989
|
+
return stableStringify(normalizeProtoConfig(left)) === stableStringify(normalizeProtoConfig(right));
|
|
990
|
+
}
|
|
911
991
|
|
|
912
992
|
//#endregion
|
|
913
993
|
//#region src/cli/commands/apply/label.ts
|
|
@@ -920,6 +1000,16 @@ function trnPrefix(workspaceId) {
|
|
|
920
1000
|
return `trn:v1:workspace:${workspaceId}`;
|
|
921
1001
|
}
|
|
922
1002
|
const sdkNameLabelKey = "sdk-name";
|
|
1003
|
+
const sdkVersionLabelKey = "sdk-version";
|
|
1004
|
+
/**
|
|
1005
|
+
* Check whether existing metadata was produced by the current SDK version.
|
|
1006
|
+
* @param existingLabels - Labels currently stored on the remote resource
|
|
1007
|
+
* @param desiredLabels - Labels that will be written by the current apply run
|
|
1008
|
+
* @returns True when sdk-version matches
|
|
1009
|
+
*/
|
|
1010
|
+
function hasMatchingSdkVersion(existingLabels, desiredLabels) {
|
|
1011
|
+
return existingLabels?.[sdkVersionLabelKey] === desiredLabels?.[sdkVersionLabelKey];
|
|
1012
|
+
}
|
|
923
1013
|
/**
|
|
924
1014
|
* Build metadata request with SDK labels.
|
|
925
1015
|
* @param trn - Target TRN
|
|
@@ -935,7 +1025,7 @@ async function buildMetaRequest(trn, appName, existingLabels) {
|
|
|
935
1025
|
labels: {
|
|
936
1026
|
...existingLabels ?? {},
|
|
937
1027
|
[sdkNameLabelKey]: appName,
|
|
938
|
-
|
|
1028
|
+
[sdkVersionLabelKey]: sdkVersion
|
|
939
1029
|
}
|
|
940
1030
|
};
|
|
941
1031
|
}
|
|
@@ -966,6 +1056,54 @@ async function applyApplication(client, changeSet, phase = "create-update") {
|
|
|
966
1056
|
function trn$6(workspaceId, name) {
|
|
967
1057
|
return `trn:v1:workspace:${workspaceId}:application:${name}`;
|
|
968
1058
|
}
|
|
1059
|
+
function sortStrings(values) {
|
|
1060
|
+
return [...values ?? []].sort();
|
|
1061
|
+
}
|
|
1062
|
+
function normalizeSubgraphs(subgraphs) {
|
|
1063
|
+
return [...subgraphs ?? []].map((subgraph) => ({
|
|
1064
|
+
serviceType: subgraph.serviceType,
|
|
1065
|
+
serviceNamespace: subgraph.serviceNamespace ?? ""
|
|
1066
|
+
})).sort((left, right) => {
|
|
1067
|
+
if (left.serviceType !== right.serviceType) return left.serviceType - right.serviceType;
|
|
1068
|
+
return left.serviceNamespace.localeCompare(right.serviceNamespace);
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
function toComparableApplication(input) {
|
|
1072
|
+
return {
|
|
1073
|
+
authNamespace: input.authNamespace,
|
|
1074
|
+
authIdpConfigName: input.authIdpConfigName,
|
|
1075
|
+
cors: sortStrings(input.cors),
|
|
1076
|
+
subgraphs: [...input.subgraphs],
|
|
1077
|
+
allowedIpAddresses: sortStrings(input.allowedIpAddresses),
|
|
1078
|
+
disableIntrospection: input.disableIntrospection,
|
|
1079
|
+
disabled: input.disabled
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
function normalizeComparableApplication(application, authNamespace, authIdpConfigName, cors) {
|
|
1083
|
+
return toComparableApplication({
|
|
1084
|
+
authNamespace: authNamespace ?? "",
|
|
1085
|
+
authIdpConfigName: authIdpConfigName ?? "",
|
|
1086
|
+
cors,
|
|
1087
|
+
subgraphs: normalizeSubgraphs(application.subgraphs.map((subgraph) => protoSubgraph(subgraph))),
|
|
1088
|
+
allowedIpAddresses: application.config.allowedIpAddresses ?? [],
|
|
1089
|
+
disableIntrospection: application.config.disableIntrospection ?? false,
|
|
1090
|
+
disabled: false
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
function normalizeComparableExistingApplication(app) {
|
|
1094
|
+
return toComparableApplication({
|
|
1095
|
+
authNamespace: app.authNamespace,
|
|
1096
|
+
authIdpConfigName: app.authIdpConfigName,
|
|
1097
|
+
cors: app.cors,
|
|
1098
|
+
subgraphs: normalizeSubgraphs(app.subgraphs),
|
|
1099
|
+
allowedIpAddresses: app.allowedIpAddresses,
|
|
1100
|
+
disableIntrospection: app.disableIntrospection,
|
|
1101
|
+
disabled: app.disabled
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
function areApplicationsEqual(existing, desired) {
|
|
1105
|
+
return areNormalizedEqual(normalizeComparableExistingApplication(existing), desired);
|
|
1106
|
+
}
|
|
969
1107
|
/**
|
|
970
1108
|
* Plan application changes based on current and desired state.
|
|
971
1109
|
* @param context - Planning context
|
|
@@ -1027,32 +1165,30 @@ async function planApplication(context) {
|
|
|
1027
1165
|
if (idpConfigs.length > 0) authIdpConfigName = idpConfigs[0].name;
|
|
1028
1166
|
}
|
|
1029
1167
|
const metaRequest = await buildMetaRequest(trn$6(workspaceId, application.name), application.name);
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1168
|
+
const resolvedCors = await resolveStaticWebsiteUrls(client, workspaceId, application.config.cors, "CORS");
|
|
1169
|
+
const desired = normalizeComparableApplication(application, authNamespace, authIdpConfigName, resolvedCors);
|
|
1170
|
+
const request = {
|
|
1171
|
+
workspaceId,
|
|
1172
|
+
applicationName: application.name,
|
|
1173
|
+
authNamespace,
|
|
1174
|
+
authIdpConfigName,
|
|
1175
|
+
cors: application.config.cors,
|
|
1176
|
+
subgraphs: application.subgraphs.map((subgraph) => protoSubgraph(subgraph)),
|
|
1177
|
+
allowedIpAddresses: application.config.allowedIpAddresses,
|
|
1178
|
+
disableIntrospection: application.config.disableIntrospection
|
|
1179
|
+
};
|
|
1180
|
+
const existing = existingApplications.find((app) => app.name === application.name);
|
|
1181
|
+
if (existing) {
|
|
1182
|
+
const { metadata } = await client.getMetadata({ trn: trn$6(workspaceId, application.name) });
|
|
1183
|
+
if (metadata?.labels?.["sdk-name"] === application.name && hasMatchingSdkVersion(metadata?.labels, metaRequest.labels) && areApplicationsEqual(existing, desired)) changeSet.unchanged.push({ name: application.name });
|
|
1184
|
+
else changeSet.updates.push({
|
|
1185
|
+
name: application.name,
|
|
1186
|
+
request,
|
|
1187
|
+
metaRequest
|
|
1188
|
+
});
|
|
1189
|
+
} else changeSet.creates.push({
|
|
1045
1190
|
name: application.name,
|
|
1046
|
-
request
|
|
1047
|
-
workspaceId,
|
|
1048
|
-
applicationName: application.name,
|
|
1049
|
-
authNamespace,
|
|
1050
|
-
authIdpConfigName,
|
|
1051
|
-
cors: application.config.cors,
|
|
1052
|
-
subgraphs: application.subgraphs.map((subgraph) => protoSubgraph(subgraph)),
|
|
1053
|
-
allowedIpAddresses: application.config.allowedIpAddresses,
|
|
1054
|
-
disableIntrospection: application.config.disableIntrospection
|
|
1055
|
-
},
|
|
1191
|
+
request,
|
|
1056
1192
|
metaRequest
|
|
1057
1193
|
});
|
|
1058
1194
|
changeSet.print();
|
|
@@ -1092,7 +1228,7 @@ const CHUNK_SIZE = 64 * 1024;
|
|
|
1092
1228
|
function computeContentHash(content) {
|
|
1093
1229
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
1094
1230
|
}
|
|
1095
|
-
function functionRegistryTrn(workspaceId, name) {
|
|
1231
|
+
function functionRegistryTrn$1(workspaceId, name) {
|
|
1096
1232
|
return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
|
|
1097
1233
|
}
|
|
1098
1234
|
/**
|
|
@@ -1198,6 +1334,16 @@ function collectFunctionEntries(application, workflowJobs, bundledScripts) {
|
|
|
1198
1334
|
return entries;
|
|
1199
1335
|
}
|
|
1200
1336
|
/**
|
|
1337
|
+
* Filter collected workflow jobs down to the ones actually bundled.
|
|
1338
|
+
* @param jobs - All collected workflow jobs
|
|
1339
|
+
* @param usedJobNames - Job names that were bundled
|
|
1340
|
+
* @returns Bundled workflow jobs only
|
|
1341
|
+
*/
|
|
1342
|
+
function filterBundledWorkflowJobs(jobs, usedJobNames) {
|
|
1343
|
+
const used = new Set(usedJobNames);
|
|
1344
|
+
return jobs.filter((job) => used.has(job.name));
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1201
1347
|
* Plan function registry changes based on current and desired state.
|
|
1202
1348
|
* @param client - Operator client instance
|
|
1203
1349
|
* @param workspaceId - Workspace ID
|
|
@@ -1228,16 +1374,18 @@ async function planFunctionRegistry(client, workspaceId, appName, entries) {
|
|
|
1228
1374
|
});
|
|
1229
1375
|
const existingMap = {};
|
|
1230
1376
|
await Promise.all(existingFunctions.map(async (func) => {
|
|
1231
|
-
const { metadata } = await client.getMetadata({ trn: functionRegistryTrn(workspaceId, func.name) });
|
|
1377
|
+
const { metadata } = await client.getMetadata({ trn: functionRegistryTrn$1(workspaceId, func.name) });
|
|
1232
1378
|
existingMap[func.name] = {
|
|
1233
1379
|
resource: func,
|
|
1234
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
1380
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
1381
|
+
allLabels: metadata?.labels
|
|
1235
1382
|
};
|
|
1236
1383
|
}));
|
|
1237
1384
|
for (const entry of entries) {
|
|
1238
1385
|
const existing = existingMap[entry.name];
|
|
1239
|
-
const metaRequest = await buildMetaRequest(functionRegistryTrn(workspaceId, entry.name), appName);
|
|
1386
|
+
const metaRequest = await buildMetaRequest(functionRegistryTrn$1(workspaceId, entry.name), appName);
|
|
1240
1387
|
if (existing) {
|
|
1388
|
+
const isManagedByApp = existing.label === appName;
|
|
1241
1389
|
if (!existing.label) unmanaged.push({
|
|
1242
1390
|
resourceType: "Function registry",
|
|
1243
1391
|
resourceName: entry.name
|
|
@@ -1247,7 +1395,8 @@ async function planFunctionRegistry(client, workspaceId, appName, entries) {
|
|
|
1247
1395
|
resourceName: entry.name,
|
|
1248
1396
|
currentOwner: existing.label
|
|
1249
1397
|
});
|
|
1250
|
-
changeSet.
|
|
1398
|
+
if (existing.resource.contentHash === entry.contentHash && isManagedByApp && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: entry.name });
|
|
1399
|
+
else changeSet.updates.push({
|
|
1251
1400
|
name: entry.name,
|
|
1252
1401
|
entry,
|
|
1253
1402
|
metaRequest
|
|
@@ -1434,10 +1583,10 @@ async function applyIdP(client, result, phase = "create-update") {
|
|
|
1434
1583
|
* @returns Planned changes and metadata
|
|
1435
1584
|
*/
|
|
1436
1585
|
async function planIdP(context) {
|
|
1437
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
1586
|
+
const { client, workspaceId, application, forRemoval, forceApplyAll = false } = context;
|
|
1438
1587
|
const idps = forRemoval ? [] : application.idpServices;
|
|
1439
1588
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$3(client, workspaceId, application.name, idps);
|
|
1440
|
-
const clientChangeSet = await planClients(client, workspaceId, idps, serviceChangeSet.deletes.map((del) => del.name));
|
|
1589
|
+
const clientChangeSet = await planClients(client, workspaceId, idps, serviceChangeSet.deletes.map((del) => del.name), forceApplyAll);
|
|
1441
1590
|
serviceChangeSet.print();
|
|
1442
1591
|
clientChangeSet.print();
|
|
1443
1592
|
return {
|
|
@@ -1453,6 +1602,49 @@ async function planIdP(context) {
|
|
|
1453
1602
|
function trn$5(workspaceId, name) {
|
|
1454
1603
|
return `trn:v1:workspace:${workspaceId}:idp:${name}`;
|
|
1455
1604
|
}
|
|
1605
|
+
function normalizeComparableUserAuthPolicy(policy) {
|
|
1606
|
+
return {
|
|
1607
|
+
useNonEmailIdentifier: policy?.useNonEmailIdentifier ?? false,
|
|
1608
|
+
allowSelfPasswordReset: policy?.allowSelfPasswordReset ?? false,
|
|
1609
|
+
passwordRequireUppercase: policy?.passwordRequireUppercase ?? false,
|
|
1610
|
+
passwordRequireLowercase: policy?.passwordRequireLowercase ?? false,
|
|
1611
|
+
passwordRequireNonAlphanumeric: policy?.passwordRequireNonAlphanumeric ?? false,
|
|
1612
|
+
passwordRequireNumeric: policy?.passwordRequireNumeric ?? false,
|
|
1613
|
+
passwordMinLength: policy?.passwordMinLength ?? 0,
|
|
1614
|
+
passwordMaxLength: policy?.passwordMaxLength ?? 0,
|
|
1615
|
+
allowedEmailDomains: [...policy?.allowedEmailDomains ?? []].sort(),
|
|
1616
|
+
allowGoogleOauth: policy?.allowGoogleOauth ?? false,
|
|
1617
|
+
disablePasswordAuth: policy?.disablePasswordAuth ?? false,
|
|
1618
|
+
allowMicrosoftOauth: policy?.allowMicrosoftOauth ?? false
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function normalizeComparableDisableGqlOperations(value) {
|
|
1622
|
+
return {
|
|
1623
|
+
create: value?.create ?? false,
|
|
1624
|
+
update: value?.update ?? false,
|
|
1625
|
+
delete: value?.delete ?? false,
|
|
1626
|
+
read: value?.read ?? false,
|
|
1627
|
+
sendPasswordResetEmail: value?.sendPasswordResetEmail ?? false
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
function normalizeComparableIdPService(input) {
|
|
1631
|
+
return {
|
|
1632
|
+
authorization: input.authorization,
|
|
1633
|
+
lang: input.lang === IdPLang.UNSPECIFIED ? IdPLang.EN : input.lang,
|
|
1634
|
+
userAuthPolicy: input.userAuthPolicy,
|
|
1635
|
+
publishUserEvents: input.publishUserEvents,
|
|
1636
|
+
disableGqlOperations: input.disableGqlOperations
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
function areIdPServicesEqual(existing, desired) {
|
|
1640
|
+
return areNormalizedEqual(normalizeComparableIdPService({
|
|
1641
|
+
authorization: existing.authorization,
|
|
1642
|
+
lang: existing.lang,
|
|
1643
|
+
userAuthPolicy: normalizeComparableUserAuthPolicy(existing.userAuthPolicy),
|
|
1644
|
+
publishUserEvents: existing.publishUserEvents,
|
|
1645
|
+
disableGqlOperations: normalizeComparableDisableGqlOperations(existing.disableGqlOperations)
|
|
1646
|
+
}), desired);
|
|
1647
|
+
}
|
|
1456
1648
|
async function planServices$3(client, workspaceId, appName, idps) {
|
|
1457
1649
|
const changeSet = createChangeSet("IdP services");
|
|
1458
1650
|
const conflicts = [];
|
|
@@ -1477,7 +1669,8 @@ async function planServices$3(client, workspaceId, appName, idps) {
|
|
|
1477
1669
|
const { metadata } = await client.getMetadata({ trn: trn$5(workspaceId, resource.namespace.name) });
|
|
1478
1670
|
existingServices[resource.namespace.name] = {
|
|
1479
1671
|
resource,
|
|
1480
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
1672
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
1673
|
+
allLabels: metadata?.labels
|
|
1481
1674
|
};
|
|
1482
1675
|
}));
|
|
1483
1676
|
for (const idp of idps) {
|
|
@@ -1498,7 +1691,25 @@ async function planServices$3(client, workspaceId, appName, idps) {
|
|
|
1498
1691
|
}
|
|
1499
1692
|
const lang = convertLang(idp.lang);
|
|
1500
1693
|
const userAuthPolicy = idp.userAuthPolicy;
|
|
1694
|
+
const publishUserEvents = idp.publishUserEvents ?? false;
|
|
1695
|
+
const desired = normalizeComparableIdPService({
|
|
1696
|
+
authorization,
|
|
1697
|
+
lang,
|
|
1698
|
+
userAuthPolicy: normalizeComparableUserAuthPolicy(userAuthPolicy),
|
|
1699
|
+
publishUserEvents,
|
|
1700
|
+
disableGqlOperations: normalizeComparableDisableGqlOperations(convertGqlOperationsToDisable(idp.gqlOperations))
|
|
1701
|
+
});
|
|
1702
|
+
const request = {
|
|
1703
|
+
workspaceId,
|
|
1704
|
+
namespaceName,
|
|
1705
|
+
authorization,
|
|
1706
|
+
lang,
|
|
1707
|
+
userAuthPolicy,
|
|
1708
|
+
publishUserEvents,
|
|
1709
|
+
disableGqlOperations: convertGqlOperationsToDisable(idp.gqlOperations)
|
|
1710
|
+
};
|
|
1501
1711
|
if (existing) {
|
|
1712
|
+
const isManagedByApp = existing.label === appName;
|
|
1502
1713
|
if (!existing.label) unmanaged.push({
|
|
1503
1714
|
resourceType: "IdP service",
|
|
1504
1715
|
resourceName: idp.name
|
|
@@ -1508,31 +1719,16 @@ async function planServices$3(client, workspaceId, appName, idps) {
|
|
|
1508
1719
|
resourceName: idp.name,
|
|
1509
1720
|
currentOwner: existing.label
|
|
1510
1721
|
});
|
|
1511
|
-
changeSet.
|
|
1722
|
+
if (isManagedByApp && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areIdPServicesEqual(existing.resource, desired)) changeSet.unchanged.push({ name: namespaceName });
|
|
1723
|
+
else changeSet.updates.push({
|
|
1512
1724
|
name: namespaceName,
|
|
1513
|
-
request
|
|
1514
|
-
workspaceId,
|
|
1515
|
-
namespaceName,
|
|
1516
|
-
authorization,
|
|
1517
|
-
lang,
|
|
1518
|
-
userAuthPolicy,
|
|
1519
|
-
publishUserEvents: idp.publishUserEvents,
|
|
1520
|
-
disableGqlOperations: convertGqlOperationsToDisable(idp.gqlOperations)
|
|
1521
|
-
},
|
|
1725
|
+
request,
|
|
1522
1726
|
metaRequest
|
|
1523
1727
|
});
|
|
1524
1728
|
delete existingServices[namespaceName];
|
|
1525
1729
|
} else changeSet.creates.push({
|
|
1526
1730
|
name: namespaceName,
|
|
1527
|
-
request
|
|
1528
|
-
workspaceId,
|
|
1529
|
-
namespaceName,
|
|
1530
|
-
authorization,
|
|
1531
|
-
lang,
|
|
1532
|
-
userAuthPolicy,
|
|
1533
|
-
publishUserEvents: idp.publishUserEvents,
|
|
1534
|
-
disableGqlOperations: convertGqlOperationsToDisable(idp.gqlOperations)
|
|
1535
|
-
},
|
|
1731
|
+
request,
|
|
1536
1732
|
metaRequest
|
|
1537
1733
|
});
|
|
1538
1734
|
}
|
|
@@ -1554,7 +1750,7 @@ async function planServices$3(client, workspaceId, appName, idps) {
|
|
|
1554
1750
|
resourceOwners
|
|
1555
1751
|
};
|
|
1556
1752
|
}
|
|
1557
|
-
async function planClients(client, workspaceId, idps, deletedServices) {
|
|
1753
|
+
async function planClients(client, workspaceId, idps, deletedServices, forceApplyAll = false) {
|
|
1558
1754
|
const changeSet = createChangeSet("IdP clients");
|
|
1559
1755
|
const fetchClients = (namespaceName) => {
|
|
1560
1756
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -1582,12 +1778,13 @@ async function planClients(client, workspaceId, idps, deletedServices) {
|
|
|
1582
1778
|
existingNameMap.set(client.name, client.clientSecret);
|
|
1583
1779
|
});
|
|
1584
1780
|
for (const name of idp.clients) if (existingNameMap.has(name)) {
|
|
1585
|
-
changeSet.updates.push({
|
|
1781
|
+
if (forceApplyAll) changeSet.updates.push({
|
|
1586
1782
|
name,
|
|
1587
1783
|
workspaceId,
|
|
1588
1784
|
namespaceName,
|
|
1589
|
-
clientSecret: existingNameMap.get(name)
|
|
1785
|
+
clientSecret: existingNameMap.get(name) ?? ""
|
|
1590
1786
|
});
|
|
1787
|
+
else changeSet.unchanged.push({ name });
|
|
1591
1788
|
existingNameMap.delete(name);
|
|
1592
1789
|
} else changeSet.creates.push({
|
|
1593
1790
|
name,
|
|
@@ -1597,7 +1794,7 @@ async function planClients(client, workspaceId, idps, deletedServices) {
|
|
|
1597
1794
|
client: { name }
|
|
1598
1795
|
}
|
|
1599
1796
|
});
|
|
1600
|
-
existingNameMap.forEach((name) => {
|
|
1797
|
+
existingNameMap.forEach((_clientSecret, name) => {
|
|
1601
1798
|
changeSet.deletes.push({
|
|
1602
1799
|
name,
|
|
1603
1800
|
request: {
|
|
@@ -1703,21 +1900,21 @@ async function applyAuth(client, result, phase = "create-update") {
|
|
|
1703
1900
|
* @returns Planned auth changes and metadata
|
|
1704
1901
|
*/
|
|
1705
1902
|
async function planAuth(context) {
|
|
1706
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
1903
|
+
const { client, workspaceId, application, forRemoval, forceApplyAll = false } = context;
|
|
1707
1904
|
const auths = [];
|
|
1708
1905
|
if (!forRemoval && application.authService) {
|
|
1709
1906
|
await application.authService.resolveNamespaces();
|
|
1710
1907
|
auths.push(application.authService);
|
|
1711
1908
|
}
|
|
1712
|
-
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, auths);
|
|
1909
|
+
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$2(client, workspaceId, application.name, auths, forceApplyAll);
|
|
1713
1910
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
1714
1911
|
const [idpConfigChangeSet, userProfileConfigChangeSet, tenantConfigChangeSet, machineUserChangeSet, authHookChangeSet, oauth2ClientChangeSet, scimChangeSet, scimResourceChangeSet] = await Promise.all([
|
|
1715
|
-
planIdPConfigs(client, workspaceId, auths, deletedServices),
|
|
1716
|
-
planUserProfileConfigs(client, workspaceId, auths, deletedServices),
|
|
1717
|
-
planTenantConfigs(client, workspaceId, auths, deletedServices),
|
|
1718
|
-
planMachineUsers(client, workspaceId, auths, deletedServices),
|
|
1719
|
-
planAuthHooks(client, workspaceId, auths, deletedServices),
|
|
1720
|
-
planOAuth2Clients(client, workspaceId, auths, deletedServices),
|
|
1912
|
+
planIdPConfigs(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1913
|
+
planUserProfileConfigs(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1914
|
+
planTenantConfigs(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1915
|
+
planMachineUsers(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1916
|
+
planAuthHooks(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1917
|
+
planOAuth2Clients(client, workspaceId, auths, deletedServices, forceApplyAll),
|
|
1721
1918
|
planSCIMConfigs(client, workspaceId, auths, deletedServices),
|
|
1722
1919
|
planSCIMResources(client, workspaceId, auths, deletedServices)
|
|
1723
1920
|
]);
|
|
@@ -1750,7 +1947,7 @@ async function planAuth(context) {
|
|
|
1750
1947
|
function trn$4(workspaceId, name) {
|
|
1751
1948
|
return `trn:v1:workspace:${workspaceId}:auth:${name}`;
|
|
1752
1949
|
}
|
|
1753
|
-
async function planServices$2(client, workspaceId, appName, auths) {
|
|
1950
|
+
async function planServices$2(client, workspaceId, appName, auths, forceApplyAll = false) {
|
|
1754
1951
|
const changeSet = createChangeSet("Auth services");
|
|
1755
1952
|
const conflicts = [];
|
|
1756
1953
|
const unmanaged = [];
|
|
@@ -1781,7 +1978,13 @@ async function planServices$2(client, workspaceId, appName, auths) {
|
|
|
1781
1978
|
const { parsedConfig: config } = auth;
|
|
1782
1979
|
const existing = existingServices[config.name];
|
|
1783
1980
|
const metaRequest = await buildMetaRequest(trn$4(workspaceId, config.name), appName);
|
|
1981
|
+
const request = {
|
|
1982
|
+
workspaceId,
|
|
1983
|
+
namespaceName: config.name,
|
|
1984
|
+
publishSessionEvents: config.publishSessionEvents
|
|
1985
|
+
};
|
|
1784
1986
|
if (existing) {
|
|
1987
|
+
const isManagedByApp = existing.label === appName;
|
|
1785
1988
|
if (!existing.label) unmanaged.push({
|
|
1786
1989
|
resourceType: "Auth service",
|
|
1787
1990
|
resourceName: config.name
|
|
@@ -1791,23 +1994,16 @@ async function planServices$2(client, workspaceId, appName, auths) {
|
|
|
1791
1994
|
resourceName: config.name,
|
|
1792
1995
|
currentOwner: existing.label
|
|
1793
1996
|
});
|
|
1794
|
-
changeSet.
|
|
1997
|
+
if (!forceApplyAll && existing.resource.publishSessionEvents === (config.publishSessionEvents ?? false) && isManagedByApp) changeSet.unchanged.push({ name: config.name });
|
|
1998
|
+
else changeSet.updates.push({
|
|
1795
1999
|
name: config.name,
|
|
1796
|
-
request
|
|
1797
|
-
workspaceId,
|
|
1798
|
-
namespaceName: config.name,
|
|
1799
|
-
publishSessionEvents: config.publishSessionEvents
|
|
1800
|
-
},
|
|
2000
|
+
request,
|
|
1801
2001
|
metaRequest
|
|
1802
2002
|
});
|
|
1803
2003
|
delete existingServices[config.name];
|
|
1804
2004
|
} else changeSet.creates.push({
|
|
1805
2005
|
name: config.name,
|
|
1806
|
-
request
|
|
1807
|
-
workspaceId,
|
|
1808
|
-
namespaceName: config.name,
|
|
1809
|
-
publishSessionEvents: config.publishSessionEvents
|
|
1810
|
-
},
|
|
2006
|
+
request,
|
|
1811
2007
|
metaRequest
|
|
1812
2008
|
});
|
|
1813
2009
|
}
|
|
@@ -1829,7 +2025,7 @@ async function planServices$2(client, workspaceId, appName, auths) {
|
|
|
1829
2025
|
resourceOwners
|
|
1830
2026
|
};
|
|
1831
2027
|
}
|
|
1832
|
-
async function planIdPConfigs(client, workspaceId, auths, deletedServices) {
|
|
2028
|
+
async function planIdPConfigs(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
1833
2029
|
const changeSet = createChangeSet("Auth idpConfigs");
|
|
1834
2030
|
const fetchIdPConfigs = (namespaceName) => {
|
|
1835
2031
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -1850,32 +2046,51 @@ async function planIdPConfigs(client, workspaceId, auths, deletedServices) {
|
|
|
1850
2046
|
for (const authService of auths) {
|
|
1851
2047
|
const { parsedConfig: config } = authService;
|
|
1852
2048
|
const existingIdPConfigs = await fetchIdPConfigs(config.name);
|
|
1853
|
-
const
|
|
2049
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
1854
2050
|
existingIdPConfigs.forEach((idpConfig) => {
|
|
1855
|
-
|
|
2051
|
+
existingMap.set(idpConfig.name, idpConfig);
|
|
1856
2052
|
});
|
|
1857
2053
|
const idpConfig = config.idProvider;
|
|
1858
|
-
if (idpConfig)
|
|
1859
|
-
|
|
2054
|
+
if (idpConfig) {
|
|
2055
|
+
const desired = protoIdPConfig(idpConfig);
|
|
2056
|
+
const existing = existingMap.get(idpConfig.name);
|
|
2057
|
+
if (existing) {
|
|
2058
|
+
const desiredComparable = await protoIdPConfigForComparison(client, workspaceId, idpConfig, desired);
|
|
2059
|
+
if (!desiredComparable) {
|
|
2060
|
+
changeSet.updates.push({
|
|
2061
|
+
name: idpConfig.name,
|
|
2062
|
+
idpConfig,
|
|
2063
|
+
request: {
|
|
2064
|
+
workspaceId,
|
|
2065
|
+
namespaceName: config.name,
|
|
2066
|
+
idpConfig: desired
|
|
2067
|
+
}
|
|
2068
|
+
});
|
|
2069
|
+
existingMap.delete(idpConfig.name);
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
if (!forceApplyAll && areAuthIdPConfigsEqual(existing, desiredComparable)) changeSet.unchanged.push({ name: idpConfig.name });
|
|
2073
|
+
else changeSet.updates.push({
|
|
2074
|
+
name: idpConfig.name,
|
|
2075
|
+
idpConfig,
|
|
2076
|
+
request: {
|
|
2077
|
+
workspaceId,
|
|
2078
|
+
namespaceName: config.name,
|
|
2079
|
+
idpConfig: desired
|
|
2080
|
+
}
|
|
2081
|
+
});
|
|
2082
|
+
existingMap.delete(idpConfig.name);
|
|
2083
|
+
} else changeSet.creates.push({
|
|
1860
2084
|
name: idpConfig.name,
|
|
1861
2085
|
idpConfig,
|
|
1862
2086
|
request: {
|
|
1863
2087
|
workspaceId,
|
|
1864
2088
|
namespaceName: config.name,
|
|
1865
|
-
idpConfig:
|
|
2089
|
+
idpConfig: desired
|
|
1866
2090
|
}
|
|
1867
2091
|
});
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
name: idpConfig.name,
|
|
1871
|
-
idpConfig,
|
|
1872
|
-
request: {
|
|
1873
|
-
workspaceId,
|
|
1874
|
-
namespaceName: config.name,
|
|
1875
|
-
idpConfig: protoIdPConfig(idpConfig)
|
|
1876
|
-
}
|
|
1877
|
-
});
|
|
1878
|
-
existingNameSet.forEach((name) => {
|
|
2092
|
+
}
|
|
2093
|
+
existingMap.forEach((_, name) => {
|
|
1879
2094
|
changeSet.deletes.push({
|
|
1880
2095
|
name,
|
|
1881
2096
|
request: {
|
|
@@ -1898,6 +2113,32 @@ async function planIdPConfigs(client, workspaceId, auths, deletedServices) {
|
|
|
1898
2113
|
});
|
|
1899
2114
|
return changeSet;
|
|
1900
2115
|
}
|
|
2116
|
+
async function protoIdPConfigForComparison(client, workspaceId, idpConfig, desired) {
|
|
2117
|
+
if (idpConfig.kind !== "BuiltInIdP") return desired;
|
|
2118
|
+
const config = await tryProtoBuiltinIdPConfig(client, workspaceId, idpConfig);
|
|
2119
|
+
return config ? {
|
|
2120
|
+
...desired,
|
|
2121
|
+
config
|
|
2122
|
+
} : void 0;
|
|
2123
|
+
}
|
|
2124
|
+
function normalizeComparableAuthIdPConfig(idpConfig) {
|
|
2125
|
+
const configCase = idpConfig.config?.config?.case;
|
|
2126
|
+
const oidcValue = configCase === "oidc" && typeof idpConfig.config?.config?.value === "object" && idpConfig.config.config.value !== null ? idpConfig.config.config.value : void 0;
|
|
2127
|
+
return normalizeProtoConfig({
|
|
2128
|
+
name: idpConfig.name,
|
|
2129
|
+
authType: idpConfig.authType,
|
|
2130
|
+
config: configCase === "oidc" ? { config: {
|
|
2131
|
+
case: "oidc",
|
|
2132
|
+
value: {
|
|
2133
|
+
...oidcValue ?? {},
|
|
2134
|
+
issuerUrl: oidcValue && "issuerUrl" in oidcValue ? oidcValue.issuerUrl || void 0 : void 0
|
|
2135
|
+
}
|
|
2136
|
+
} } : idpConfig.config
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
function areAuthIdPConfigsEqual(existing, desired) {
|
|
2140
|
+
return areNormalizedEqual(normalizeComparableAuthIdPConfig(existing), normalizeComparableAuthIdPConfig(desired));
|
|
2141
|
+
}
|
|
1901
2142
|
function protoIdPConfig(idpConfig) {
|
|
1902
2143
|
switch (idpConfig.kind) {
|
|
1903
2144
|
case "IDToken": return {
|
|
@@ -1950,6 +2191,11 @@ function protoIdPConfig(idpConfig) {
|
|
|
1950
2191
|
}
|
|
1951
2192
|
}
|
|
1952
2193
|
async function protoBuiltinIdPConfig(client, workspaceId, builtinIdPConfig) {
|
|
2194
|
+
const config = await tryProtoBuiltinIdPConfig(client, workspaceId, builtinIdPConfig);
|
|
2195
|
+
if (!config) throw new Error(`Built-in IdP "${builtinIdPConfig.namespace}" not found. Please ensure that idp is configured correctly.`);
|
|
2196
|
+
return config;
|
|
2197
|
+
}
|
|
2198
|
+
async function tryProtoBuiltinIdPConfig(client, workspaceId, builtinIdPConfig) {
|
|
1953
2199
|
let idpService;
|
|
1954
2200
|
try {
|
|
1955
2201
|
idpService = await client.getIdPService({
|
|
@@ -1957,14 +2203,20 @@ async function protoBuiltinIdPConfig(client, workspaceId, builtinIdPConfig) {
|
|
|
1957
2203
|
namespaceName: builtinIdPConfig.namespace
|
|
1958
2204
|
});
|
|
1959
2205
|
} catch (error) {
|
|
1960
|
-
if (error instanceof ConnectError && error.code === Code.NotFound)
|
|
2206
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
2207
|
+
throw error;
|
|
2208
|
+
}
|
|
2209
|
+
let idpClient;
|
|
2210
|
+
try {
|
|
2211
|
+
idpClient = await client.getIdPClient({
|
|
2212
|
+
workspaceId,
|
|
2213
|
+
namespaceName: builtinIdPConfig.namespace,
|
|
2214
|
+
name: builtinIdPConfig.clientName
|
|
2215
|
+
});
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) return;
|
|
1961
2218
|
throw error;
|
|
1962
2219
|
}
|
|
1963
|
-
const idpClient = await client.getIdPClient({
|
|
1964
|
-
workspaceId,
|
|
1965
|
-
namespaceName: builtinIdPConfig.namespace,
|
|
1966
|
-
name: builtinIdPConfig.clientName
|
|
1967
|
-
});
|
|
1968
2220
|
const vaultName = idpClientVaultName(builtinIdPConfig.namespace, builtinIdPConfig.clientName);
|
|
1969
2221
|
const secretKey = idpClientSecretName(builtinIdPConfig.namespace, builtinIdPConfig.clientName);
|
|
1970
2222
|
return { config: {
|
|
@@ -1980,16 +2232,35 @@ async function protoBuiltinIdPConfig(client, workspaceId, builtinIdPConfig) {
|
|
|
1980
2232
|
}
|
|
1981
2233
|
} };
|
|
1982
2234
|
}
|
|
1983
|
-
async function planUserProfileConfigs(client, workspaceId, auths, deletedServices) {
|
|
2235
|
+
async function planUserProfileConfigs(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
1984
2236
|
const changeSet = createChangeSet("Auth userProfileConfigs");
|
|
1985
2237
|
for (const auth of auths) {
|
|
1986
2238
|
const { parsedConfig: config } = auth;
|
|
1987
2239
|
const name = `${config.name}-user-profile-config`;
|
|
1988
2240
|
try {
|
|
1989
|
-
await client.getUserProfileConfig({
|
|
2241
|
+
const { userProfileProviderConfig } = await client.getUserProfileConfig({
|
|
1990
2242
|
workspaceId,
|
|
1991
2243
|
namespaceName: config.name
|
|
1992
2244
|
});
|
|
2245
|
+
const userProfileForUpdate = auth.userProfile;
|
|
2246
|
+
if (userProfileForUpdate) {
|
|
2247
|
+
const desired = protoUserProfileConfig(userProfileForUpdate);
|
|
2248
|
+
if (!forceApplyAll && areUserProfileConfigsEqual(userProfileProviderConfig ?? {}, desired)) changeSet.unchanged.push({ name });
|
|
2249
|
+
else changeSet.updates.push({
|
|
2250
|
+
name,
|
|
2251
|
+
request: {
|
|
2252
|
+
workspaceId,
|
|
2253
|
+
namespaceName: config.name,
|
|
2254
|
+
userProfileProviderConfig: desired
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2257
|
+
} else changeSet.deletes.push({
|
|
2258
|
+
name,
|
|
2259
|
+
request: {
|
|
2260
|
+
workspaceId,
|
|
2261
|
+
namespaceName: config.name
|
|
2262
|
+
}
|
|
2263
|
+
});
|
|
1993
2264
|
} catch (error) {
|
|
1994
2265
|
if (error instanceof ConnectError && error.code === Code.NotFound) {
|
|
1995
2266
|
const userProfileForCreate = auth.userProfile;
|
|
@@ -2005,22 +2276,6 @@ async function planUserProfileConfigs(client, workspaceId, auths, deletedService
|
|
|
2005
2276
|
}
|
|
2006
2277
|
throw error;
|
|
2007
2278
|
}
|
|
2008
|
-
const userProfileForUpdate = auth.userProfile;
|
|
2009
|
-
if (userProfileForUpdate) changeSet.updates.push({
|
|
2010
|
-
name,
|
|
2011
|
-
request: {
|
|
2012
|
-
workspaceId,
|
|
2013
|
-
namespaceName: config.name,
|
|
2014
|
-
userProfileProviderConfig: protoUserProfileConfig(userProfileForUpdate)
|
|
2015
|
-
}
|
|
2016
|
-
});
|
|
2017
|
-
else changeSet.deletes.push({
|
|
2018
|
-
name,
|
|
2019
|
-
request: {
|
|
2020
|
-
workspaceId,
|
|
2021
|
-
namespaceName: config.name
|
|
2022
|
-
}
|
|
2023
|
-
});
|
|
2024
2279
|
}
|
|
2025
2280
|
for (const namespaceName of deletedServices) {
|
|
2026
2281
|
try {
|
|
@@ -2060,16 +2315,34 @@ function protoUserProfileConfig(userProfile) {
|
|
|
2060
2315
|
} }
|
|
2061
2316
|
};
|
|
2062
2317
|
}
|
|
2063
|
-
async function planTenantConfigs(client, workspaceId, auths, deletedServices) {
|
|
2318
|
+
async function planTenantConfigs(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
2064
2319
|
const changeSet = createChangeSet("Auth tenantConfigs");
|
|
2065
2320
|
for (const auth of auths) {
|
|
2066
2321
|
const { parsedConfig: config } = auth;
|
|
2067
2322
|
const name = `${config.name}-tenant-config`;
|
|
2068
2323
|
try {
|
|
2069
|
-
await client.getTenantConfig({
|
|
2324
|
+
const { tenantProviderConfig } = await client.getTenantConfig({
|
|
2070
2325
|
workspaceId,
|
|
2071
2326
|
namespaceName: config.name
|
|
2072
2327
|
});
|
|
2328
|
+
if (config.tenantProvider) {
|
|
2329
|
+
const desired = protoTenantConfig(config.tenantProvider);
|
|
2330
|
+
if (!forceApplyAll && areTenantProviderConfigsEqual(tenantProviderConfig, desired)) changeSet.unchanged.push({ name });
|
|
2331
|
+
else changeSet.updates.push({
|
|
2332
|
+
name,
|
|
2333
|
+
request: {
|
|
2334
|
+
workspaceId,
|
|
2335
|
+
namespaceName: config.name,
|
|
2336
|
+
tenantProviderConfig: desired
|
|
2337
|
+
}
|
|
2338
|
+
});
|
|
2339
|
+
} else changeSet.deletes.push({
|
|
2340
|
+
name,
|
|
2341
|
+
request: {
|
|
2342
|
+
workspaceId,
|
|
2343
|
+
namespaceName: config.name
|
|
2344
|
+
}
|
|
2345
|
+
});
|
|
2073
2346
|
} catch (error) {
|
|
2074
2347
|
if (error instanceof ConnectError && error.code === Code.NotFound) {
|
|
2075
2348
|
if (config.tenantProvider) changeSet.creates.push({
|
|
@@ -2084,21 +2357,6 @@ async function planTenantConfigs(client, workspaceId, auths, deletedServices) {
|
|
|
2084
2357
|
}
|
|
2085
2358
|
throw error;
|
|
2086
2359
|
}
|
|
2087
|
-
if (config.tenantProvider) changeSet.updates.push({
|
|
2088
|
-
name,
|
|
2089
|
-
request: {
|
|
2090
|
-
workspaceId,
|
|
2091
|
-
namespaceName: config.name,
|
|
2092
|
-
tenantProviderConfig: protoTenantConfig(config.tenantProvider)
|
|
2093
|
-
}
|
|
2094
|
-
});
|
|
2095
|
-
else changeSet.deletes.push({
|
|
2096
|
-
name,
|
|
2097
|
-
request: {
|
|
2098
|
-
workspaceId,
|
|
2099
|
-
namespaceName: config.name
|
|
2100
|
-
}
|
|
2101
|
-
});
|
|
2102
2360
|
}
|
|
2103
2361
|
for (const namespaceName of deletedServices) {
|
|
2104
2362
|
try {
|
|
@@ -2133,7 +2391,7 @@ function protoTenantConfig(tenantConfig) {
|
|
|
2133
2391
|
} }
|
|
2134
2392
|
};
|
|
2135
2393
|
}
|
|
2136
|
-
async function planMachineUsers(client, workspaceId, auths, deletedServices) {
|
|
2394
|
+
async function planMachineUsers(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
2137
2395
|
const changeSet = createChangeSet("Auth machineUsers");
|
|
2138
2396
|
const fetchMachineUsers = (authNamespace) => {
|
|
2139
2397
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -2154,25 +2412,31 @@ async function planMachineUsers(client, workspaceId, auths, deletedServices) {
|
|
|
2154
2412
|
for (const auth of auths) {
|
|
2155
2413
|
const { parsedConfig: config } = auth;
|
|
2156
2414
|
const existingMachineUsers = await fetchMachineUsers(config.name);
|
|
2157
|
-
const
|
|
2415
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
2158
2416
|
existingMachineUsers.forEach((machineUser) => {
|
|
2159
|
-
|
|
2417
|
+
existingMap.set(machineUser.name, machineUser);
|
|
2160
2418
|
});
|
|
2161
2419
|
for (const machineUsername of Object.keys(config.machineUsers ?? {})) {
|
|
2162
2420
|
const machineUser = config.machineUsers?.[machineUsername];
|
|
2163
2421
|
if (!machineUser) continue;
|
|
2164
|
-
|
|
2165
|
-
|
|
2422
|
+
const desiredMachineUser = {
|
|
2423
|
+
attributes: machineUser.attributeList,
|
|
2424
|
+
attributeMap: machineUser.attributes ? protoMachineUserAttributeMap(machineUser.attributes) : void 0
|
|
2425
|
+
};
|
|
2426
|
+
const existing = existingMap.get(machineUsername);
|
|
2427
|
+
if (existing) {
|
|
2428
|
+
if (!forceApplyAll && areMachineUsersEqual(existing, desiredMachineUser)) changeSet.unchanged.push({ name: machineUsername });
|
|
2429
|
+
else changeSet.updates.push({
|
|
2166
2430
|
name: machineUsername,
|
|
2167
2431
|
request: {
|
|
2168
2432
|
workspaceId,
|
|
2169
2433
|
authNamespace: config.name,
|
|
2170
2434
|
name: machineUsername,
|
|
2171
2435
|
attributes: machineUser.attributeList,
|
|
2172
|
-
attributeMap:
|
|
2436
|
+
attributeMap: desiredMachineUser.attributeMap
|
|
2173
2437
|
}
|
|
2174
2438
|
});
|
|
2175
|
-
|
|
2439
|
+
existingMap.delete(machineUsername);
|
|
2176
2440
|
} else changeSet.creates.push({
|
|
2177
2441
|
name: machineUsername,
|
|
2178
2442
|
request: {
|
|
@@ -2180,11 +2444,11 @@ async function planMachineUsers(client, workspaceId, auths, deletedServices) {
|
|
|
2180
2444
|
authNamespace: config.name,
|
|
2181
2445
|
name: machineUsername,
|
|
2182
2446
|
attributes: machineUser.attributeList,
|
|
2183
|
-
attributeMap:
|
|
2447
|
+
attributeMap: desiredMachineUser.attributeMap
|
|
2184
2448
|
}
|
|
2185
2449
|
});
|
|
2186
2450
|
}
|
|
2187
|
-
|
|
2451
|
+
existingMap.forEach((_, name) => {
|
|
2188
2452
|
changeSet.deletes.push({
|
|
2189
2453
|
name,
|
|
2190
2454
|
request: {
|
|
@@ -2212,7 +2476,60 @@ function protoMachineUserAttributeMap(attributeMap) {
|
|
|
2212
2476
|
for (const [key, value] of Object.entries(attributeMap)) ret[key] = fromJson(ValueSchema, value ?? null);
|
|
2213
2477
|
return ret;
|
|
2214
2478
|
}
|
|
2215
|
-
|
|
2479
|
+
function normalizeComparableUserProfileConfig(config) {
|
|
2480
|
+
const comparableConfig = config.config?.config;
|
|
2481
|
+
const tailorDBConfig = comparableConfig?.case === "tailordb" ? comparableConfig.value : void 0;
|
|
2482
|
+
return normalizeProtoConfig({
|
|
2483
|
+
providerType: config.providerType,
|
|
2484
|
+
config: { config: {
|
|
2485
|
+
case: comparableConfig?.case,
|
|
2486
|
+
value: tailorDBConfig ? {
|
|
2487
|
+
...tailorDBConfig,
|
|
2488
|
+
tenantIdField: tailorDBConfig.tenantIdField || void 0,
|
|
2489
|
+
attributesFields: normalizeStringArray(tailorDBConfig.attributesFields),
|
|
2490
|
+
attributeMap: normalizeProtoConfig(tailorDBConfig.attributeMap)
|
|
2491
|
+
} : comparableConfig?.value
|
|
2492
|
+
} }
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
function areUserProfileConfigsEqual(existing, desired) {
|
|
2496
|
+
return areNormalizedEqual(normalizeComparableUserProfileConfig(existing), normalizeComparableUserProfileConfig(desired));
|
|
2497
|
+
}
|
|
2498
|
+
function normalizeComparableTenantProviderConfig(config) {
|
|
2499
|
+
return normalizeProtoConfig(config);
|
|
2500
|
+
}
|
|
2501
|
+
function areTenantProviderConfigsEqual(existing, desired) {
|
|
2502
|
+
return areNormalizedEqual(normalizeComparableTenantProviderConfig(existing), normalizeComparableTenantProviderConfig(desired));
|
|
2503
|
+
}
|
|
2504
|
+
function normalizeComparableMachineUser(input) {
|
|
2505
|
+
return normalizeProtoConfig({
|
|
2506
|
+
attributes: normalizeStringArray(input.attributes),
|
|
2507
|
+
attributeMap: normalizeProtoConfig(input.attributeMap ?? {})
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
function areMachineUsersEqual(existing, desired) {
|
|
2511
|
+
return areNormalizedEqual(normalizeComparableMachineUser(existing), normalizeComparableMachineUser(desired));
|
|
2512
|
+
}
|
|
2513
|
+
function normalizeComparableOAuth2Client(client) {
|
|
2514
|
+
const accessTokenLifetime = oauth2LifetimeToSeconds(client.accessTokenLifetime);
|
|
2515
|
+
const refreshTokenLifetime = oauth2LifetimeToSeconds(client.refreshTokenLifetime);
|
|
2516
|
+
return normalizeProtoConfig({
|
|
2517
|
+
...client,
|
|
2518
|
+
redirectUris: normalizeStringArray(client.redirectUris),
|
|
2519
|
+
grantTypes: [...client.grantTypes ?? []].sort((left, right) => left - right),
|
|
2520
|
+
accessTokenLifetime: accessTokenLifetime ?? 86400,
|
|
2521
|
+
refreshTokenLifetime: refreshTokenLifetime ?? 604800,
|
|
2522
|
+
requireDpop: client.requireDpop ?? false
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
function oauth2LifetimeToSeconds(lifetime) {
|
|
2526
|
+
if (typeof lifetime === "number") return lifetime;
|
|
2527
|
+
if (lifetime?.seconds != null) return Number(lifetime.seconds);
|
|
2528
|
+
}
|
|
2529
|
+
function areOAuth2ClientsEqual(existing, desired) {
|
|
2530
|
+
return areNormalizedEqual(normalizeComparableOAuth2Client(existing), normalizeComparableOAuth2Client(desired));
|
|
2531
|
+
}
|
|
2532
|
+
async function planOAuth2Clients(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
2216
2533
|
const changeSet = createChangeSet("Auth oauth2Clients");
|
|
2217
2534
|
const fetchOAuth2Clients = (namespaceName) => {
|
|
2218
2535
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -2235,14 +2552,16 @@ async function planOAuth2Clients(client, workspaceId, auths, deletedServices) {
|
|
|
2235
2552
|
const existingOAuth2Clients = await fetchOAuth2Clients(config.name);
|
|
2236
2553
|
const existingClientsMap = /* @__PURE__ */ new Map();
|
|
2237
2554
|
existingOAuth2Clients.forEach((oauth2Client) => {
|
|
2238
|
-
existingClientsMap.set(oauth2Client.name, oauth2Client
|
|
2555
|
+
existingClientsMap.set(oauth2Client.name, oauth2Client);
|
|
2239
2556
|
});
|
|
2240
2557
|
for (const oauth2ClientName of Object.keys(config.oauth2Clients ?? {})) {
|
|
2241
2558
|
const oauth2Client = config.oauth2Clients?.[oauth2ClientName];
|
|
2242
2559
|
if (!oauth2Client) continue;
|
|
2243
2560
|
const newOAuth2Client = protoOAuth2Client(oauth2ClientName, oauth2Client);
|
|
2561
|
+
const resolvedRedirectUris = await resolveStaticWebsiteUrls(client, workspaceId, newOAuth2Client.redirectUris ?? [], "OAuth2 redirect URIs");
|
|
2244
2562
|
if (existingClientsMap.has(oauth2ClientName)) {
|
|
2245
|
-
|
|
2563
|
+
const existingClient = existingClientsMap.get(oauth2ClientName);
|
|
2564
|
+
if (existingClient.clientType !== newOAuth2Client.clientType) changeSet.replaces.push({
|
|
2246
2565
|
name: oauth2ClientName,
|
|
2247
2566
|
deleteRequest: {
|
|
2248
2567
|
workspaceId,
|
|
@@ -2255,14 +2574,33 @@ async function planOAuth2Clients(client, workspaceId, auths, deletedServices) {
|
|
|
2255
2574
|
oauth2Client: newOAuth2Client
|
|
2256
2575
|
}
|
|
2257
2576
|
});
|
|
2258
|
-
else
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2577
|
+
else {
|
|
2578
|
+
const desiredComparable = {
|
|
2579
|
+
...newOAuth2Client,
|
|
2580
|
+
redirectUris: resolvedRedirectUris,
|
|
2581
|
+
accessTokenLifetime: oauth2LifetimeToSeconds(newOAuth2Client.accessTokenLifetime),
|
|
2582
|
+
refreshTokenLifetime: oauth2LifetimeToSeconds(newOAuth2Client.refreshTokenLifetime)
|
|
2583
|
+
};
|
|
2584
|
+
const existingComparable = {
|
|
2585
|
+
name: existingClient.name,
|
|
2586
|
+
description: existingClient.description,
|
|
2587
|
+
grantTypes: existingClient.grantTypes,
|
|
2588
|
+
redirectUris: existingClient.redirectUris,
|
|
2589
|
+
clientType: existingClient.clientType,
|
|
2590
|
+
accessTokenLifetime: oauth2LifetimeToSeconds(existingClient.accessTokenLifetime),
|
|
2591
|
+
refreshTokenLifetime: oauth2LifetimeToSeconds(existingClient.refreshTokenLifetime),
|
|
2592
|
+
requireDpop: existingClient.requireDpop
|
|
2593
|
+
};
|
|
2594
|
+
if (!forceApplyAll && areOAuth2ClientsEqual(existingComparable, desiredComparable)) changeSet.unchanged.push({ name: oauth2ClientName });
|
|
2595
|
+
else changeSet.updates.push({
|
|
2596
|
+
name: oauth2ClientName,
|
|
2597
|
+
request: {
|
|
2598
|
+
workspaceId,
|
|
2599
|
+
namespaceName: config.name,
|
|
2600
|
+
oauth2Client: newOAuth2Client
|
|
2601
|
+
}
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2266
2604
|
existingClientsMap.delete(oauth2ClientName);
|
|
2267
2605
|
} else changeSet.creates.push({
|
|
2268
2606
|
name: oauth2ClientName,
|
|
@@ -2537,21 +2875,36 @@ function protoSCIMAttribute(attr) {
|
|
|
2537
2875
|
subAttributes: attr.subAttributes?.map((attr) => protoSCIMAttribute(attr))
|
|
2538
2876
|
};
|
|
2539
2877
|
}
|
|
2540
|
-
|
|
2878
|
+
function areAuthHooksEqual(existing, desired) {
|
|
2879
|
+
return areNormalizedEqual({
|
|
2880
|
+
scriptRef: existing.scriptRef ?? "",
|
|
2881
|
+
invoker: existing.invoker ? {
|
|
2882
|
+
namespace: existing.invoker.namespace ?? "",
|
|
2883
|
+
machineUserName: existing.invoker.machineUserName ?? ""
|
|
2884
|
+
} : void 0
|
|
2885
|
+
}, {
|
|
2886
|
+
scriptRef: desired.scriptRef ?? "",
|
|
2887
|
+
invoker: desired.invoker ? {
|
|
2888
|
+
namespace: desired.invoker.namespace ?? "",
|
|
2889
|
+
machineUserName: desired.invoker.machineUserName ?? ""
|
|
2890
|
+
} : void 0
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
async function planAuthHooks(client, workspaceId, auths, deletedServices, forceApplyAll = false) {
|
|
2541
2894
|
const changeSet = createChangeSet("Auth hooks");
|
|
2542
2895
|
for (const auth of auths) {
|
|
2543
2896
|
const { parsedConfig: config } = auth;
|
|
2544
2897
|
const beforeLogin = config.hooks?.beforeLogin;
|
|
2545
2898
|
let existingHook;
|
|
2546
2899
|
try {
|
|
2547
|
-
await client.getAuthHook({
|
|
2900
|
+
const { hook } = await client.getAuthHook({
|
|
2548
2901
|
workspaceId,
|
|
2549
2902
|
namespaceName: config.name,
|
|
2550
2903
|
hookPoint: AuthHookPoint.BEFORE_LOGIN
|
|
2551
2904
|
});
|
|
2552
|
-
existingHook =
|
|
2905
|
+
existingHook = hook;
|
|
2553
2906
|
} catch (error) {
|
|
2554
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) existingHook =
|
|
2907
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) existingHook = void 0;
|
|
2555
2908
|
else throw error;
|
|
2556
2909
|
}
|
|
2557
2910
|
if (beforeLogin) {
|
|
@@ -2567,7 +2920,8 @@ async function planAuthHooks(client, workspaceId, auths, deletedServices) {
|
|
|
2567
2920
|
}
|
|
2568
2921
|
}
|
|
2569
2922
|
};
|
|
2570
|
-
if (existingHook) changeSet.
|
|
2923
|
+
if (existingHook) if (!forceApplyAll && areAuthHooksEqual(existingHook, hookRequest.hook)) changeSet.unchanged.push({ name: `${config.name}/before-login` });
|
|
2924
|
+
else changeSet.updates.push({
|
|
2571
2925
|
name: `${config.name}/before-login`,
|
|
2572
2926
|
request: hookRequest
|
|
2573
2927
|
});
|
|
@@ -2750,19 +3104,10 @@ const ACTOR_TRANSFORM_EXPR = "actor: args.actor ? (({ attributeMap, attributes:
|
|
|
2750
3104
|
function buildExecutorArgsExpr(triggerKind, env) {
|
|
2751
3105
|
const envExpr = `env: ${JSON.stringify(env)}`;
|
|
2752
3106
|
switch (triggerKind) {
|
|
2753
|
-
case "schedule":
|
|
2754
|
-
case "recordCreated":
|
|
2755
|
-
case "recordUpdated":
|
|
2756
|
-
case "recordDeleted":
|
|
2757
|
-
case "idpUserCreated":
|
|
2758
|
-
case "idpUserUpdated":
|
|
2759
|
-
case "idpUserDeleted":
|
|
2760
|
-
case "authAccessTokenIssued":
|
|
2761
|
-
case "authAccessTokenRefreshed":
|
|
2762
|
-
case "authAccessTokenRevoked": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, ${envExpr} })`;
|
|
3107
|
+
case "schedule": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, ${envExpr} })`;
|
|
2763
3108
|
case "resolverExecuted": return `({ ...args, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, success: !!args.succeeded, result: args.succeeded?.result.resolver, error: args.failed?.error, ${envExpr} })`;
|
|
2764
3109
|
case "incomingWebhook": return `({ ...args, appNamespace: args.namespaceName, rawBody: args.raw_body, ${envExpr} })`;
|
|
2765
|
-
default:
|
|
3110
|
+
default: return `({ ...args, event: args.eventType?.split(".").pop(), rawEvent: args.eventType, appNamespace: args.namespaceName, ${ACTOR_TRANSFORM_EXPR}, ${envExpr} })`;
|
|
2766
3111
|
}
|
|
2767
3112
|
}
|
|
2768
3113
|
/**
|
|
@@ -2832,13 +3177,15 @@ async function planExecutor(context) {
|
|
|
2832
3177
|
const { metadata } = await client.getMetadata({ trn: trn$3(workspaceId, resource.name) });
|
|
2833
3178
|
existingExecutors[resource.name] = {
|
|
2834
3179
|
resource,
|
|
2835
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
3180
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
3181
|
+
allLabels: metadata?.labels
|
|
2836
3182
|
};
|
|
2837
3183
|
}));
|
|
2838
3184
|
const executors = forRemoval ? {} : await application.executorService?.loadExecutors() ?? {};
|
|
2839
3185
|
for (const executor of Object.values(executors)) {
|
|
2840
3186
|
const existing = existingExecutors[executor.name];
|
|
2841
3187
|
const metaRequest = await buildMetaRequest(trn$3(workspaceId, executor.name), application.name);
|
|
3188
|
+
const desiredExecutor = protoExecutor(application, executor);
|
|
2842
3189
|
if (existing) {
|
|
2843
3190
|
if (!existing.label) unmanaged.push({
|
|
2844
3191
|
resourceType: "Executor",
|
|
@@ -2849,11 +3196,12 @@ async function planExecutor(context) {
|
|
|
2849
3196
|
resourceName: executor.name,
|
|
2850
3197
|
currentOwner: existing.label
|
|
2851
3198
|
});
|
|
2852
|
-
changeSet.
|
|
3199
|
+
if (existing.label === application.name && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areExecutorsEqual(existing.resource, desiredExecutor)) changeSet.unchanged.push({ name: executor.name });
|
|
3200
|
+
else changeSet.updates.push({
|
|
2853
3201
|
name: executor.name,
|
|
2854
3202
|
request: {
|
|
2855
3203
|
workspaceId,
|
|
2856
|
-
executor:
|
|
3204
|
+
executor: desiredExecutor
|
|
2857
3205
|
},
|
|
2858
3206
|
metaRequest
|
|
2859
3207
|
});
|
|
@@ -2862,7 +3210,7 @@ async function planExecutor(context) {
|
|
|
2862
3210
|
name: executor.name,
|
|
2863
3211
|
request: {
|
|
2864
3212
|
workspaceId,
|
|
2865
|
-
executor:
|
|
3213
|
+
executor: desiredExecutor
|
|
2866
3214
|
},
|
|
2867
3215
|
metaRequest
|
|
2868
3216
|
});
|
|
@@ -2886,6 +3234,56 @@ async function planExecutor(context) {
|
|
|
2886
3234
|
resourceOwners
|
|
2887
3235
|
};
|
|
2888
3236
|
}
|
|
3237
|
+
function normalizeComparableExecutor(executor) {
|
|
3238
|
+
const normalized = normalizeProtoConfig(executor) ?? {};
|
|
3239
|
+
const webhookHeaders = normalized.targetConfig?.config?.case === "webhook" ? [...normalized.targetConfig.config.value.headers ?? []].sort((left, right) => (left.key ?? "").localeCompare(right.key ?? "")) : void 0;
|
|
3240
|
+
const triggerConfig = normalized.triggerConfig?.config?.case === "incomingWebhook" ? {
|
|
3241
|
+
...normalized.triggerConfig,
|
|
3242
|
+
config: {
|
|
3243
|
+
...normalized.triggerConfig.config,
|
|
3244
|
+
value: {}
|
|
3245
|
+
}
|
|
3246
|
+
} : normalized.triggerConfig?.config?.case === "event" ? {
|
|
3247
|
+
...normalized.triggerConfig,
|
|
3248
|
+
config: {
|
|
3249
|
+
...normalized.triggerConfig.config,
|
|
3250
|
+
value: {
|
|
3251
|
+
...normalized.triggerConfig.config.value,
|
|
3252
|
+
eventType: void 0
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
} : normalized.triggerConfig;
|
|
3256
|
+
return {
|
|
3257
|
+
name: normalized.name,
|
|
3258
|
+
description: normalized.description ?? "",
|
|
3259
|
+
disabled: normalized.disabled ?? false,
|
|
3260
|
+
triggerType: normalized.triggerType,
|
|
3261
|
+
triggerConfig,
|
|
3262
|
+
targetType: normalized.targetType,
|
|
3263
|
+
targetConfig: normalized.targetConfig?.config?.case === "webhook" ? {
|
|
3264
|
+
...normalized.targetConfig,
|
|
3265
|
+
config: {
|
|
3266
|
+
...normalized.targetConfig.config,
|
|
3267
|
+
value: {
|
|
3268
|
+
...normalized.targetConfig.config.value,
|
|
3269
|
+
headers: webhookHeaders
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
} : normalized.targetConfig?.config?.case === "function" ? {
|
|
3273
|
+
...normalized.targetConfig,
|
|
3274
|
+
config: {
|
|
3275
|
+
...normalized.targetConfig.config,
|
|
3276
|
+
value: {
|
|
3277
|
+
...normalized.targetConfig.config.value,
|
|
3278
|
+
script: void 0
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
} : normalized.targetConfig
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function areExecutorsEqual(existing, desired) {
|
|
3285
|
+
return areNormalizedEqual(normalizeComparableExecutor(existing), normalizeComparableExecutor(desired));
|
|
3286
|
+
}
|
|
2889
3287
|
function resolveTailorDBNamespace(application, typeName) {
|
|
2890
3288
|
for (const service of application.tailorDBServices) if (service.types[typeName]) return service.namespace;
|
|
2891
3289
|
throw new Error(`TailorDB type "${typeName}" not found in any namespace. Available namespaces: ${application.tailorDBServices.map((s) => s.namespace).join(", ")}`);
|
|
@@ -2910,18 +3308,6 @@ function protoExecutor(application, executor) {
|
|
|
2910
3308
|
let triggerType;
|
|
2911
3309
|
let triggerConfig;
|
|
2912
3310
|
const argsExpr = buildExecutorArgsExpr(trigger.kind, env);
|
|
2913
|
-
const eventType = {
|
|
2914
|
-
recordCreated: "tailordb.type_record.created",
|
|
2915
|
-
recordUpdated: "tailordb.type_record.updated",
|
|
2916
|
-
recordDeleted: "tailordb.type_record.deleted",
|
|
2917
|
-
resolverExecuted: "pipeline.resolver.executed",
|
|
2918
|
-
idpUserCreated: "idp.user.created",
|
|
2919
|
-
idpUserUpdated: "idp.user.updated",
|
|
2920
|
-
idpUserDeleted: "idp.user.deleted",
|
|
2921
|
-
authAccessTokenIssued: "auth.access_token.issued",
|
|
2922
|
-
authAccessTokenRefreshed: "auth.access_token.refreshed",
|
|
2923
|
-
authAccessTokenRevoked: "auth.access_token.revoked"
|
|
2924
|
-
};
|
|
2925
3311
|
function typedEventTrigger(typedConfig) {
|
|
2926
3312
|
return { config: {
|
|
2927
3313
|
case: "event",
|
|
@@ -2939,14 +3325,12 @@ function protoExecutor(application, executor) {
|
|
|
2939
3325
|
}
|
|
2940
3326
|
} };
|
|
2941
3327
|
break;
|
|
2942
|
-
case "
|
|
2943
|
-
case "recordUpdated":
|
|
2944
|
-
case "recordDeleted":
|
|
3328
|
+
case "tailordb":
|
|
2945
3329
|
triggerType = ExecutorTriggerType.EVENT;
|
|
2946
3330
|
triggerConfig = typedEventTrigger({
|
|
2947
3331
|
case: "tailordb",
|
|
2948
3332
|
value: {
|
|
2949
|
-
eventTypes:
|
|
3333
|
+
eventTypes: trigger.events,
|
|
2950
3334
|
namespaceName: resolveTailorDBNamespace(application, trigger.typeName),
|
|
2951
3335
|
typeName: trigger.typeName,
|
|
2952
3336
|
...trigger.condition ? { condition: { expr: `(${stringifyFunction(trigger.condition)})(${argsExpr})` } } : {}
|
|
@@ -2958,7 +3342,7 @@ function protoExecutor(application, executor) {
|
|
|
2958
3342
|
triggerConfig = typedEventTrigger({
|
|
2959
3343
|
case: "pipeline",
|
|
2960
3344
|
value: {
|
|
2961
|
-
eventTypes: [
|
|
3345
|
+
eventTypes: ["pipeline.resolver.executed"],
|
|
2962
3346
|
namespaceName: resolveResolverNamespace(application, trigger.resolverName),
|
|
2963
3347
|
resolverName: trigger.resolverName,
|
|
2964
3348
|
...trigger.condition ? { condition: { expr: `(${stringifyFunction(trigger.condition)})(${argsExpr})` } } : {}
|
|
@@ -2972,26 +3356,22 @@ function protoExecutor(application, executor) {
|
|
|
2972
3356
|
value: {}
|
|
2973
3357
|
} };
|
|
2974
3358
|
break;
|
|
2975
|
-
case "
|
|
2976
|
-
case "idpUserUpdated":
|
|
2977
|
-
case "idpUserDeleted":
|
|
3359
|
+
case "idpUser":
|
|
2978
3360
|
triggerType = ExecutorTriggerType.EVENT;
|
|
2979
3361
|
triggerConfig = typedEventTrigger({
|
|
2980
3362
|
case: "idp",
|
|
2981
3363
|
value: {
|
|
2982
|
-
eventTypes:
|
|
3364
|
+
eventTypes: trigger.events,
|
|
2983
3365
|
namespaceName: resolveIdpNamespace(application)
|
|
2984
3366
|
}
|
|
2985
3367
|
});
|
|
2986
3368
|
break;
|
|
2987
|
-
case "
|
|
2988
|
-
case "authAccessTokenRefreshed":
|
|
2989
|
-
case "authAccessTokenRevoked":
|
|
3369
|
+
case "authAccessToken":
|
|
2990
3370
|
triggerType = ExecutorTriggerType.EVENT;
|
|
2991
3371
|
triggerConfig = typedEventTrigger({
|
|
2992
3372
|
case: "auth",
|
|
2993
3373
|
value: {
|
|
2994
|
-
eventTypes:
|
|
3374
|
+
eventTypes: trigger.events,
|
|
2995
3375
|
namespaceName: resolveAuthNamespace(application)
|
|
2996
3376
|
}
|
|
2997
3377
|
});
|
|
@@ -3147,7 +3527,7 @@ async function applyPipeline(client, result, phase = "create-update") {
|
|
|
3147
3527
|
* @returns Planned changes
|
|
3148
3528
|
*/
|
|
3149
3529
|
async function planPipeline(context) {
|
|
3150
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
3530
|
+
const { client, workspaceId, application, forRemoval, forceApplyAll = false } = context;
|
|
3151
3531
|
const pipelines = [];
|
|
3152
3532
|
if (!forRemoval) for (const pipeline of application.resolverServices) {
|
|
3153
3533
|
await pipeline.loadResolvers();
|
|
@@ -3155,7 +3535,7 @@ async function planPipeline(context) {
|
|
|
3155
3535
|
}
|
|
3156
3536
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
3157
3537
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices$1(client, workspaceId, application.name, pipelines);
|
|
3158
|
-
const resolverChangeSet = await planResolvers(client, workspaceId, pipelines, executors, serviceChangeSet.deletes.map((del) => del.name), application.env);
|
|
3538
|
+
const resolverChangeSet = await planResolvers(client, workspaceId, pipelines, executors, serviceChangeSet.deletes.map((del) => del.name), application.env, forceApplyAll);
|
|
3159
3539
|
serviceChangeSet.print();
|
|
3160
3540
|
resolverChangeSet.print();
|
|
3161
3541
|
return {
|
|
@@ -3195,7 +3575,8 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
|
3195
3575
|
const { metadata } = await client.getMetadata({ trn: trn$2(workspaceId, resource.namespace.name) });
|
|
3196
3576
|
existingServices[resource.namespace.name] = {
|
|
3197
3577
|
resource,
|
|
3198
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
3578
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
3579
|
+
allLabels: metadata?.labels
|
|
3199
3580
|
};
|
|
3200
3581
|
}));
|
|
3201
3582
|
for (const pipeline of pipelines) {
|
|
@@ -3211,7 +3592,8 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
|
3211
3592
|
resourceName: pipeline.namespace,
|
|
3212
3593
|
currentOwner: existing.label
|
|
3213
3594
|
});
|
|
3214
|
-
changeSet.
|
|
3595
|
+
if (existing.label === appName && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) changeSet.unchanged.push({ name: pipeline.namespace });
|
|
3596
|
+
else changeSet.updates.push({
|
|
3215
3597
|
name: pipeline.namespace,
|
|
3216
3598
|
request: {
|
|
3217
3599
|
workspaceId,
|
|
@@ -3247,7 +3629,7 @@ async function planServices$1(client, workspaceId, appName, pipelines) {
|
|
|
3247
3629
|
resourceOwners
|
|
3248
3630
|
};
|
|
3249
3631
|
}
|
|
3250
|
-
async function planResolvers(client, workspaceId, pipelines, executors, deletedServices, env) {
|
|
3632
|
+
async function planResolvers(client, workspaceId, pipelines, executors, deletedServices, env, forceApplyAll = false) {
|
|
3251
3633
|
const changeSet = createChangeSet("Pipeline resolvers");
|
|
3252
3634
|
const fetchResolvers = (namespaceName) => {
|
|
3253
3635
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -3270,29 +3652,35 @@ async function planResolvers(client, workspaceId, pipelines, executors, deletedS
|
|
|
3270
3652
|
for (const pipeline of pipelines) for (const resolver of Object.values(pipeline.resolvers)) if (executorUsedResolvers.has(resolver.name) && resolver.publishEvents === false) throw new Error(`Resolver "${resolver.name}" has publishEvents set to false, but it is used by an executor with a resolverExecuted trigger. Either remove the publishEvents: false setting or remove the executor trigger for this resolver.`);
|
|
3271
3653
|
for (const pipeline of pipelines) {
|
|
3272
3654
|
const existingResolvers = await fetchResolvers(pipeline.namespace);
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3655
|
+
const existingResolversMap = new Map(existingResolvers.map((resolver) => [resolver.name, resolver]));
|
|
3656
|
+
for (const resolver of Object.values(pipeline.resolvers)) {
|
|
3657
|
+
const desiredResolver = processResolver(pipeline.namespace, resolver, executorUsedResolvers, env);
|
|
3658
|
+
if (existingResolversMap.get(resolver.name)) {
|
|
3659
|
+
const { pipelineResolver: existingResolverDetail } = await client.getPipelineResolver({
|
|
3660
|
+
workspaceId,
|
|
3661
|
+
namespaceName: pipeline.namespace,
|
|
3662
|
+
resolverName: resolver.name
|
|
3663
|
+
});
|
|
3664
|
+
if (!forceApplyAll && existingResolverDetail && areResolversEqual(existingResolverDetail, desiredResolver)) changeSet.unchanged.push({ name: resolver.name });
|
|
3665
|
+
else changeSet.updates.push({
|
|
3666
|
+
name: resolver.name,
|
|
3667
|
+
request: {
|
|
3668
|
+
workspaceId,
|
|
3669
|
+
namespaceName: pipeline.namespace,
|
|
3670
|
+
pipelineResolver: desiredResolver
|
|
3671
|
+
}
|
|
3672
|
+
});
|
|
3673
|
+
existingResolversMap.delete(resolver.name);
|
|
3674
|
+
} else changeSet.creates.push({
|
|
3279
3675
|
name: resolver.name,
|
|
3280
3676
|
request: {
|
|
3281
3677
|
workspaceId,
|
|
3282
3678
|
namespaceName: pipeline.namespace,
|
|
3283
|
-
pipelineResolver:
|
|
3679
|
+
pipelineResolver: desiredResolver
|
|
3284
3680
|
}
|
|
3285
3681
|
});
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
name: resolver.name,
|
|
3289
|
-
request: {
|
|
3290
|
-
workspaceId,
|
|
3291
|
-
namespaceName: pipeline.namespace,
|
|
3292
|
-
pipelineResolver: processResolver(pipeline.namespace, resolver, executorUsedResolvers, env)
|
|
3293
|
-
}
|
|
3294
|
-
});
|
|
3295
|
-
existingNameSet.forEach((name) => {
|
|
3682
|
+
}
|
|
3683
|
+
existingResolversMap.forEach((_resolver, name) => {
|
|
3296
3684
|
changeSet.deletes.push({
|
|
3297
3685
|
name,
|
|
3298
3686
|
request: {
|
|
@@ -3315,6 +3703,59 @@ async function planResolvers(client, workspaceId, pipelines, executors, deletedS
|
|
|
3315
3703
|
});
|
|
3316
3704
|
return changeSet;
|
|
3317
3705
|
}
|
|
3706
|
+
function normalizeComparableResolver(resolver) {
|
|
3707
|
+
const normalized = normalizeProtoConfig(resolver) ?? {};
|
|
3708
|
+
return {
|
|
3709
|
+
name: normalized.name,
|
|
3710
|
+
description: normalized.description ?? "",
|
|
3711
|
+
authorization: normalized.authorization ?? "",
|
|
3712
|
+
operationType: normalized.operationType,
|
|
3713
|
+
publishExecutionEvents: normalized.publishExecutionEvents ?? false,
|
|
3714
|
+
inputs: normalizeComparableFields(normalized.inputs),
|
|
3715
|
+
response: normalizeComparableField(normalized.response),
|
|
3716
|
+
pipelines: normalizeComparablePipelines(normalized.pipelines)
|
|
3717
|
+
};
|
|
3718
|
+
}
|
|
3719
|
+
function areResolversEqual(existing, desired) {
|
|
3720
|
+
return areNormalizedEqual(normalizeComparableResolver(existing), normalizeComparableResolver(desired));
|
|
3721
|
+
}
|
|
3722
|
+
function normalizeComparablePipelines(pipelines) {
|
|
3723
|
+
return (pipelines ?? []).map((pipeline) => ({
|
|
3724
|
+
name: pipeline.name ?? "",
|
|
3725
|
+
operationName: pipeline.operationName ?? "",
|
|
3726
|
+
description: pipeline.description ?? "",
|
|
3727
|
+
operationType: pipeline.operationType,
|
|
3728
|
+
operationSourceRef: pipeline.operationSourceRef ?? "",
|
|
3729
|
+
operationHook: pipeline.operationHook?.expr ?? "",
|
|
3730
|
+
postScript: pipeline.postScript ?? "",
|
|
3731
|
+
skipOperationOnError: pipeline.skipOperationOnError ?? false,
|
|
3732
|
+
invoker: pipeline.invoker ?? void 0
|
|
3733
|
+
}));
|
|
3734
|
+
}
|
|
3735
|
+
function normalizeComparableFields(fields) {
|
|
3736
|
+
return (fields ?? []).map((field) => normalizeComparableField(field));
|
|
3737
|
+
}
|
|
3738
|
+
function normalizeComparableField(field) {
|
|
3739
|
+
if (!field) return;
|
|
3740
|
+
return {
|
|
3741
|
+
name: field.name ?? "",
|
|
3742
|
+
array: field.array ?? false,
|
|
3743
|
+
required: field.required ?? true,
|
|
3744
|
+
description: field.description ?? "",
|
|
3745
|
+
type: normalizeComparableType(field.type)
|
|
3746
|
+
};
|
|
3747
|
+
}
|
|
3748
|
+
function normalizeComparableType(type) {
|
|
3749
|
+
if (!type) return;
|
|
3750
|
+
return {
|
|
3751
|
+
kind: type.kind ?? "",
|
|
3752
|
+
name: type.name ?? "",
|
|
3753
|
+
required: type.required ?? true,
|
|
3754
|
+
description: type.description ?? "",
|
|
3755
|
+
allowedValues: type.allowedValues ?? [],
|
|
3756
|
+
fields: (type.fields ?? []).map((field) => normalizeComparableField(field))
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3318
3759
|
function processResolver(namespace, resolver, executorUsedResolvers, env) {
|
|
3319
3760
|
const pipelines = [{
|
|
3320
3761
|
name: "body",
|
|
@@ -3430,7 +3871,7 @@ function hashValue(value) {
|
|
|
3430
3871
|
* @returns Planned changes for vaults and secrets
|
|
3431
3872
|
*/
|
|
3432
3873
|
async function planSecretManager(context) {
|
|
3433
|
-
const { client, workspaceId, application, forRemoval } = context;
|
|
3874
|
+
const { client, workspaceId, application, forRemoval, forceApplyAll = false } = context;
|
|
3434
3875
|
const secretVaults = forRemoval ? [] : application.secrets;
|
|
3435
3876
|
const vaultChangeSet = createChangeSet("Secret Manager vaults");
|
|
3436
3877
|
const secretChangeSet = createChangeSet("Secret Manager secrets");
|
|
@@ -3452,10 +3893,11 @@ async function planSecretManager(context) {
|
|
|
3452
3893
|
});
|
|
3453
3894
|
const existingVaults = {};
|
|
3454
3895
|
await Promise.all(existingVaultList.map(async (resource) => {
|
|
3455
|
-
const { metadata } = await client.getMetadata({ trn: vaultTrn(workspaceId, resource.name) });
|
|
3896
|
+
const { metadata } = await client.getMetadata({ trn: vaultTrn$1(workspaceId, resource.name) });
|
|
3456
3897
|
existingVaults[resource.name] = {
|
|
3457
3898
|
resource,
|
|
3458
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
3899
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
3900
|
+
allLabels: metadata?.labels
|
|
3459
3901
|
};
|
|
3460
3902
|
}));
|
|
3461
3903
|
const state = loadSecretsState();
|
|
@@ -3463,6 +3905,7 @@ async function planSecretManager(context) {
|
|
|
3463
3905
|
const vaultName = vault.vaultName;
|
|
3464
3906
|
const existing = existingVaults[vaultName];
|
|
3465
3907
|
if (existing) {
|
|
3908
|
+
const metaRequest = await buildMetaRequest(vaultTrn$1(workspaceId, vaultName), application.name);
|
|
3466
3909
|
if (!existing.label) unmanaged.push({
|
|
3467
3910
|
resourceType: "Secret Manager vault",
|
|
3468
3911
|
resourceName: vaultName
|
|
@@ -3472,7 +3915,8 @@ async function planSecretManager(context) {
|
|
|
3472
3915
|
resourceName: vaultName,
|
|
3473
3916
|
currentOwner: existing.label
|
|
3474
3917
|
});
|
|
3475
|
-
vaultChangeSet.
|
|
3918
|
+
if (existing.label === application.name && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels)) vaultChangeSet.unchanged.push({ name: vaultName });
|
|
3919
|
+
else vaultChangeSet.updates.push({
|
|
3476
3920
|
name: vaultName,
|
|
3477
3921
|
workspaceId
|
|
3478
3922
|
});
|
|
@@ -3498,7 +3942,9 @@ async function planSecretManager(context) {
|
|
|
3498
3942
|
})).map((s) => s.name);
|
|
3499
3943
|
const existingSet = new Set(existingSecrets);
|
|
3500
3944
|
for (const secret of vault.secrets) if (existingSet.has(secret.name)) {
|
|
3501
|
-
|
|
3945
|
+
const currentHash = hashValue(secret.value);
|
|
3946
|
+
const storedHash = state.vaults[vaultName]?.[secret.name];
|
|
3947
|
+
if (forceApplyAll || currentHash !== storedHash) secretChangeSet.updates.push({
|
|
3502
3948
|
name: `${vaultName}/${secret.name}`,
|
|
3503
3949
|
secretName: secret.name,
|
|
3504
3950
|
workspaceId,
|
|
@@ -3561,7 +4007,7 @@ async function planSecretManager(context) {
|
|
|
3561
4007
|
resourceOwners
|
|
3562
4008
|
};
|
|
3563
4009
|
}
|
|
3564
|
-
function vaultTrn(workspaceId, name) {
|
|
4010
|
+
function vaultTrn$1(workspaceId, name) {
|
|
3565
4011
|
return `trn:v1:workspace:${workspaceId}:vault:${name}`;
|
|
3566
4012
|
}
|
|
3567
4013
|
/**
|
|
@@ -3581,12 +4027,12 @@ async function applySecretManager(client, result, phase = "create-update", appli
|
|
|
3581
4027
|
secretmanagerVaultName: create.name
|
|
3582
4028
|
});
|
|
3583
4029
|
if (application) {
|
|
3584
|
-
const metaRequest = await buildMetaRequest(vaultTrn(create.workspaceId, create.name), application.name);
|
|
4030
|
+
const metaRequest = await buildMetaRequest(vaultTrn$1(create.workspaceId, create.name), application.name);
|
|
3585
4031
|
await client.setMetadata(metaRequest);
|
|
3586
4032
|
}
|
|
3587
4033
|
}));
|
|
3588
4034
|
if (application) await Promise.all(vaultChangeSet.updates.map(async (update) => {
|
|
3589
|
-
const metaRequest = await buildMetaRequest(vaultTrn(update.workspaceId, update.name), application.name);
|
|
4035
|
+
const metaRequest = await buildMetaRequest(vaultTrn$1(update.workspaceId, update.name), application.name);
|
|
3590
4036
|
await client.setMetadata(metaRequest);
|
|
3591
4037
|
}));
|
|
3592
4038
|
await Promise.all(secretChangeSet.creates.map((create) => client.createSecretManagerSecret({
|
|
@@ -3654,6 +4100,21 @@ async function applyStaticWebsite(client, result, phase = "create-update") {
|
|
|
3654
4100
|
function trn$1(workspaceId, name) {
|
|
3655
4101
|
return `trn:v1:workspace:${workspaceId}:staticwebsite:${name}`;
|
|
3656
4102
|
}
|
|
4103
|
+
function normalizeComparableStaticWebsiteShape(input) {
|
|
4104
|
+
return {
|
|
4105
|
+
description: input.description,
|
|
4106
|
+
allowedIpAddresses: [...input.allowedIpAddresses].sort()
|
|
4107
|
+
};
|
|
4108
|
+
}
|
|
4109
|
+
function normalizeComparableStaticWebsite(input) {
|
|
4110
|
+
return normalizeComparableStaticWebsiteShape({
|
|
4111
|
+
description: input.description || "",
|
|
4112
|
+
allowedIpAddresses: [...input.allowedIpAddresses || []]
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
function areStaticWebsitesEqual(existing, desired) {
|
|
4116
|
+
return areNormalizedEqual(normalizeComparableStaticWebsite(existing), normalizeComparableStaticWebsite(desired));
|
|
4117
|
+
}
|
|
3657
4118
|
/**
|
|
3658
4119
|
* Plan static website changes based on current and desired state.
|
|
3659
4120
|
* @param context - Planning context
|
|
@@ -3683,7 +4144,8 @@ async function planStaticWebsite(context) {
|
|
|
3683
4144
|
const { metadata } = await client.getMetadata({ trn: trn$1(workspaceId, resource.name) });
|
|
3684
4145
|
existingWebsites[resource.name] = {
|
|
3685
4146
|
resource,
|
|
3686
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
4147
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
4148
|
+
allLabels: metadata?.labels
|
|
3687
4149
|
};
|
|
3688
4150
|
}));
|
|
3689
4151
|
const staticWebsiteServices = forRemoval ? [] : application.staticWebsiteServices;
|
|
@@ -3692,7 +4154,17 @@ async function planStaticWebsite(context) {
|
|
|
3692
4154
|
const name = websiteService.name;
|
|
3693
4155
|
const existing = existingWebsites[name];
|
|
3694
4156
|
const metaRequest = await buildMetaRequest(trn$1(workspaceId, name), application.name);
|
|
4157
|
+
const desired = normalizeComparableStaticWebsite(config);
|
|
4158
|
+
const request = {
|
|
4159
|
+
workspaceId,
|
|
4160
|
+
staticwebsite: {
|
|
4161
|
+
name,
|
|
4162
|
+
description: config.description || "",
|
|
4163
|
+
allowedIpAddresses: config.allowedIpAddresses || []
|
|
4164
|
+
}
|
|
4165
|
+
};
|
|
3695
4166
|
if (existing) {
|
|
4167
|
+
const isManagedByApp = existing.label === application.name;
|
|
3696
4168
|
if (!existing.label) unmanaged.push({
|
|
3697
4169
|
resourceType: "StaticWebsite",
|
|
3698
4170
|
resourceName: name
|
|
@@ -3702,29 +4174,16 @@ async function planStaticWebsite(context) {
|
|
|
3702
4174
|
resourceName: name,
|
|
3703
4175
|
currentOwner: existing.label
|
|
3704
4176
|
});
|
|
3705
|
-
changeSet.
|
|
4177
|
+
if (isManagedByApp && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areStaticWebsitesEqual(existing.resource, desired)) changeSet.unchanged.push({ name });
|
|
4178
|
+
else changeSet.updates.push({
|
|
3706
4179
|
name,
|
|
3707
|
-
request
|
|
3708
|
-
workspaceId,
|
|
3709
|
-
staticwebsite: {
|
|
3710
|
-
name,
|
|
3711
|
-
description: config.description || "",
|
|
3712
|
-
allowedIpAddresses: config.allowedIpAddresses || []
|
|
3713
|
-
}
|
|
3714
|
-
},
|
|
4180
|
+
request,
|
|
3715
4181
|
metaRequest
|
|
3716
4182
|
});
|
|
3717
4183
|
delete existingWebsites[name];
|
|
3718
4184
|
} else changeSet.creates.push({
|
|
3719
4185
|
name,
|
|
3720
|
-
request
|
|
3721
|
-
workspaceId,
|
|
3722
|
-
staticwebsite: {
|
|
3723
|
-
name,
|
|
3724
|
-
description: config.description || "",
|
|
3725
|
-
allowedIpAddresses: config.allowedIpAddresses || []
|
|
3726
|
-
}
|
|
3727
|
-
},
|
|
4186
|
+
request,
|
|
3728
4187
|
metaRequest
|
|
3729
4188
|
});
|
|
3730
4189
|
}
|
|
@@ -3948,8 +4407,11 @@ const INITIAL_SCHEMA_NUMBER = 0;
|
|
|
3948
4407
|
* Migration file names (used within migration directories)
|
|
3949
4408
|
*/
|
|
3950
4409
|
const SCHEMA_FILE_NAME = "schema.json";
|
|
4410
|
+
/** File name for migration diff metadata. */
|
|
3951
4411
|
const DIFF_FILE_NAME = "diff.json";
|
|
4412
|
+
/** File name for migration script. */
|
|
3952
4413
|
const MIGRATE_FILE_NAME = "migrate.ts";
|
|
4414
|
+
/** File name for generated DB type definitions. */
|
|
3953
4415
|
const DB_TYPES_FILE_NAME = "db.ts";
|
|
3954
4416
|
/**
|
|
3955
4417
|
* Pattern for validating migration number format (4-digit sequential number)
|
|
@@ -5555,13 +6017,12 @@ function buildMigrationContextForScripts(client, migrationContext, migrationsReq
|
|
|
5555
6017
|
async function applyTailorDB(client, result, phase = "create-update") {
|
|
5556
6018
|
const { changeSet, context: migrationContext } = result;
|
|
5557
6019
|
if (phase === "create-update") {
|
|
5558
|
-
let pendingMigrations = [];
|
|
5559
6020
|
const typesByNamespace = /* @__PURE__ */ new Map();
|
|
5560
6021
|
for (const tailordb of migrationContext.application.tailorDBServices) {
|
|
5561
6022
|
const types = tailordb.types;
|
|
5562
6023
|
if (types) typesByNamespace.set(tailordb.namespace, types);
|
|
5563
6024
|
}
|
|
5564
|
-
pendingMigrations = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck);
|
|
6025
|
+
const pendingMigrations = await validateAndDetectMigrations(client, migrationContext.workspaceId, typesByNamespace, migrationContext.config, migrationContext.noSchemaCheck);
|
|
5565
6026
|
if (pendingMigrations.length > 0) {
|
|
5566
6027
|
processedTypes.reset();
|
|
5567
6028
|
deletedResources.reset();
|
|
@@ -5848,7 +6309,7 @@ async function executeSingleMigrationPostPhase(client, changeSet, migration) {
|
|
|
5848
6309
|
* @returns Planned changes
|
|
5849
6310
|
*/
|
|
5850
6311
|
async function planTailorDB(context) {
|
|
5851
|
-
const { client, workspaceId, application, forRemoval, config, noSchemaCheck } = context;
|
|
6312
|
+
const { client, workspaceId, application, forRemoval, config, noSchemaCheck, forceApplyAll = false } = context;
|
|
5852
6313
|
const tailordbs = [];
|
|
5853
6314
|
if (!forRemoval) for (const tailordb of application.tailorDBServices) {
|
|
5854
6315
|
await tailordb.loadTypes();
|
|
@@ -5857,7 +6318,7 @@ async function planTailorDB(context) {
|
|
|
5857
6318
|
const executors = forRemoval ? [] : Object.values(await application.executorService?.loadExecutors() ?? {});
|
|
5858
6319
|
const { changeSet: serviceChangeSet, conflicts, unmanaged, resourceOwners } = await planServices(client, workspaceId, application.name, tailordbs);
|
|
5859
6320
|
const deletedServices = serviceChangeSet.deletes.map((del) => del.name);
|
|
5860
|
-
const [typeChangeSet, gqlPermissionChangeSet] = await Promise.all([planTypes(client, workspaceId, tailordbs, executors, deletedServices), planGqlPermissions(client, workspaceId, tailordbs, deletedServices)]);
|
|
6321
|
+
const [typeChangeSet, gqlPermissionChangeSet] = await Promise.all([planTypes(client, workspaceId, tailordbs, executors, deletedServices, void 0, forceApplyAll), planGqlPermissions(client, workspaceId, tailordbs, deletedServices, forceApplyAll)]);
|
|
5861
6322
|
serviceChangeSet.print();
|
|
5862
6323
|
typeChangeSet.print();
|
|
5863
6324
|
gqlPermissionChangeSet.print();
|
|
@@ -5881,6 +6342,21 @@ async function planTailorDB(context) {
|
|
|
5881
6342
|
function trn(workspaceId, name) {
|
|
5882
6343
|
return `${trnPrefix(workspaceId)}:tailordb:${name}`;
|
|
5883
6344
|
}
|
|
6345
|
+
function normalizeComparableTailorDBService(service) {
|
|
6346
|
+
return normalizeProtoConfig({
|
|
6347
|
+
namespace: service.namespace,
|
|
6348
|
+
defaultTimezone: service.defaultTimezone || "UTC"
|
|
6349
|
+
});
|
|
6350
|
+
}
|
|
6351
|
+
function areTailorDBServicesEqual(existing, desired) {
|
|
6352
|
+
return areNormalizedEqual(normalizeComparableTailorDBService({
|
|
6353
|
+
namespace: existing.namespace?.name,
|
|
6354
|
+
defaultTimezone: existing.defaultTimezone
|
|
6355
|
+
}), normalizeComparableTailorDBService({
|
|
6356
|
+
namespace: desired.namespace,
|
|
6357
|
+
defaultTimezone: "UTC"
|
|
6358
|
+
}));
|
|
6359
|
+
}
|
|
5884
6360
|
async function planServices(client, workspaceId, appName, tailordbs) {
|
|
5885
6361
|
const changeSet = createChangeSet("TailorDB services");
|
|
5886
6362
|
const conflicts = [];
|
|
@@ -5922,7 +6398,8 @@ async function planServices(client, workspaceId, appName, tailordbs) {
|
|
|
5922
6398
|
resourceName: tailordb.namespace,
|
|
5923
6399
|
currentOwner: existing.label
|
|
5924
6400
|
});
|
|
5925
|
-
changeSet.
|
|
6401
|
+
if (existing.label === appName && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && areTailorDBServicesEqual(existing.resource, tailordb)) changeSet.unchanged.push({ name: tailordb.namespace });
|
|
6402
|
+
else changeSet.updates.push({
|
|
5926
6403
|
name: tailordb.namespace,
|
|
5927
6404
|
metaRequest
|
|
5928
6405
|
});
|
|
@@ -5955,7 +6432,7 @@ async function planServices(client, workspaceId, appName, tailordbs) {
|
|
|
5955
6432
|
resourceOwners
|
|
5956
6433
|
};
|
|
5957
6434
|
}
|
|
5958
|
-
async function planTypes(client, workspaceId, tailordbs, executors, deletedServices, filteredTypesByNamespace) {
|
|
6435
|
+
async function planTypes(client, workspaceId, tailordbs, executors, deletedServices, filteredTypesByNamespace, forceApplyAll = false) {
|
|
5959
6436
|
const changeSet = createChangeSet("TailorDB types");
|
|
5960
6437
|
const fetchTypes = (namespaceName) => {
|
|
5961
6438
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -5974,7 +6451,7 @@ async function planTypes(client, workspaceId, tailordbs, executors, deletedServi
|
|
|
5974
6451
|
});
|
|
5975
6452
|
};
|
|
5976
6453
|
const executorUsedTypes = /* @__PURE__ */ new Set();
|
|
5977
|
-
for (const executor of executors) if (executor.trigger.kind === "
|
|
6454
|
+
for (const executor of executors) if (executor.trigger.kind === "tailordb") executorUsedTypes.add(executor.trigger.typeName);
|
|
5978
6455
|
for (const tailordb of tailordbs) {
|
|
5979
6456
|
const types = filteredTypesByNamespace?.get(tailordb.namespace) ?? tailordb.types;
|
|
5980
6457
|
for (const typeName of Object.keys(types)) {
|
|
@@ -5984,13 +6461,14 @@ async function planTypes(client, workspaceId, tailordbs, executors, deletedServi
|
|
|
5984
6461
|
}
|
|
5985
6462
|
for (const tailordb of tailordbs) {
|
|
5986
6463
|
const existingTypes = await fetchTypes(tailordb.namespace);
|
|
5987
|
-
const
|
|
5988
|
-
existingTypes.forEach((type) => existingNameSet.add(type.name));
|
|
6464
|
+
const existingTypesMap = new Map(existingTypes.map((type) => [type.name, type]));
|
|
5989
6465
|
const types = filteredTypesByNamespace?.get(tailordb.namespace) ?? tailordb.types;
|
|
5990
6466
|
for (const typeName of Object.keys(types)) {
|
|
5991
6467
|
const tailordbType = generateTailorDBTypeManifest(types[typeName], executorUsedTypes, tailordb.config.gqlOperations);
|
|
5992
|
-
|
|
5993
|
-
|
|
6468
|
+
const existingType = existingTypesMap.get(typeName);
|
|
6469
|
+
if (existingType) {
|
|
6470
|
+
if (!forceApplyAll && areNormalizedEqual(normalizeComparableTailorDBType(existingType), normalizeComparableTailorDBType(tailordbType))) changeSet.unchanged.push({ name: typeName });
|
|
6471
|
+
else changeSet.updates.push({
|
|
5994
6472
|
name: typeName,
|
|
5995
6473
|
request: {
|
|
5996
6474
|
workspaceId,
|
|
@@ -5998,7 +6476,7 @@ async function planTypes(client, workspaceId, tailordbs, executors, deletedServi
|
|
|
5998
6476
|
tailordbType
|
|
5999
6477
|
}
|
|
6000
6478
|
});
|
|
6001
|
-
|
|
6479
|
+
existingTypesMap.delete(typeName);
|
|
6002
6480
|
} else changeSet.creates.push({
|
|
6003
6481
|
name: typeName,
|
|
6004
6482
|
request: {
|
|
@@ -6008,7 +6486,7 @@ async function planTypes(client, workspaceId, tailordbs, executors, deletedServi
|
|
|
6008
6486
|
}
|
|
6009
6487
|
});
|
|
6010
6488
|
}
|
|
6011
|
-
|
|
6489
|
+
existingTypesMap.forEach((_type, name) => {
|
|
6012
6490
|
changeSet.deletes.push({
|
|
6013
6491
|
name,
|
|
6014
6492
|
request: {
|
|
@@ -6031,6 +6509,66 @@ async function planTypes(client, workspaceId, tailordbs, executors, deletedServi
|
|
|
6031
6509
|
});
|
|
6032
6510
|
return changeSet;
|
|
6033
6511
|
}
|
|
6512
|
+
function isPlainObject(value) {
|
|
6513
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6514
|
+
}
|
|
6515
|
+
const tailordbCompareKnownDefaults = {
|
|
6516
|
+
disableGqlOperations: {
|
|
6517
|
+
create: false,
|
|
6518
|
+
update: false,
|
|
6519
|
+
delete: false,
|
|
6520
|
+
read: false
|
|
6521
|
+
},
|
|
6522
|
+
emptyExpression: "",
|
|
6523
|
+
numericStringPaths: new Set([
|
|
6524
|
+
"schema.fields.*.serial.start",
|
|
6525
|
+
"schema.fields.*.serial.maxValue",
|
|
6526
|
+
"schema.settings.defaultQueryLimitSize",
|
|
6527
|
+
"schema.settings.maxBulkUpsertSize"
|
|
6528
|
+
])
|
|
6529
|
+
};
|
|
6530
|
+
function normalizeComparableTailorDBType(type) {
|
|
6531
|
+
const normalized = normalizeProtoConfig(type);
|
|
6532
|
+
return normalizeTailorDBCompareValue({
|
|
6533
|
+
name: normalized?.name ?? "",
|
|
6534
|
+
schema: {
|
|
6535
|
+
description: normalized?.schema?.description ?? "",
|
|
6536
|
+
fields: normalized?.schema?.fields ?? {},
|
|
6537
|
+
relationships: normalized?.schema?.relationships ?? {},
|
|
6538
|
+
settings: normalized?.schema?.settings ?? {},
|
|
6539
|
+
indexes: normalized?.schema?.indexes ?? {},
|
|
6540
|
+
files: normalized?.schema?.files ?? {},
|
|
6541
|
+
permission: normalized?.schema?.permission ?? {}
|
|
6542
|
+
}
|
|
6543
|
+
}, []);
|
|
6544
|
+
}
|
|
6545
|
+
function normalizeTailorDBCompareValue(value, path) {
|
|
6546
|
+
if (value === void 0 || value === null) return value;
|
|
6547
|
+
if (typeof value === "number" || typeof value === "bigint" || typeof value === "string") {
|
|
6548
|
+
if (matchesNumericStringPath(path) && isNumericLikeValue(value)) return String(value);
|
|
6549
|
+
if (path.at(-1) === "expr" && value === tailordbCompareKnownDefaults.emptyExpression) return;
|
|
6550
|
+
return value;
|
|
6551
|
+
}
|
|
6552
|
+
if (Array.isArray(value)) return value.map((item, index) => normalizeTailorDBCompareValue(item, [...path, index])).filter((item) => item !== void 0);
|
|
6553
|
+
if (!isPlainObject(value)) return value;
|
|
6554
|
+
const normalizedEntries = Object.entries(value).map(([key, entryValue]) => [key, normalizeTailorDBCompareValue(entryValue, [...path, key])]).filter(([, entryValue]) => entryValue !== void 0);
|
|
6555
|
+
const normalizedObject = Object.fromEntries(normalizedEntries);
|
|
6556
|
+
if (path.at(-1) === "fields" && Object.keys(normalizedObject).length === 0) return;
|
|
6557
|
+
if (path.at(-1) === "disableGqlOperations" && areNormalizedEqual(normalizedObject, tailordbCompareKnownDefaults.disableGqlOperations)) return;
|
|
6558
|
+
return normalizedObject;
|
|
6559
|
+
}
|
|
6560
|
+
function matchesNumericStringPath(path) {
|
|
6561
|
+
const pathKey = path.map((segment) => String(segment)).join(".");
|
|
6562
|
+
return [...tailordbCompareKnownDefaults.numericStringPaths].some((pattern) => {
|
|
6563
|
+
const patternParts = pattern.split(".");
|
|
6564
|
+
const pathParts = pathKey.split(".");
|
|
6565
|
+
if (patternParts.length !== pathParts.length) return false;
|
|
6566
|
+
return patternParts.every((part, index) => part === "*" || part === pathParts[index]);
|
|
6567
|
+
});
|
|
6568
|
+
}
|
|
6569
|
+
function isNumericLikeValue(value) {
|
|
6570
|
+
return typeof value === "number" || typeof value === "bigint" || /^-?\d+$/.test(value);
|
|
6571
|
+
}
|
|
6034
6572
|
/**
|
|
6035
6573
|
* Generate a TailorDB type manifest from parsed type
|
|
6036
6574
|
* @param {TailorDBType} type - Parsed TailorDB type
|
|
@@ -6267,7 +6805,7 @@ function protoOperand(operand) {
|
|
|
6267
6805
|
value: fromJson(ValueSchema, operand)
|
|
6268
6806
|
} };
|
|
6269
6807
|
}
|
|
6270
|
-
async function planGqlPermissions(client, workspaceId, tailordbs, deletedServices) {
|
|
6808
|
+
async function planGqlPermissions(client, workspaceId, tailordbs, deletedServices, forceApplyAll = false) {
|
|
6271
6809
|
const changeSet = createChangeSet("TailorDB gqlPermissions");
|
|
6272
6810
|
const fetchGqlPermissions = (namespaceName) => {
|
|
6273
6811
|
return fetchAll(async (pageToken, maxPageSize) => {
|
|
@@ -6295,14 +6833,17 @@ async function planGqlPermissions(client, workspaceId, tailordbs, deletedService
|
|
|
6295
6833
|
for (const typeName of Object.keys(types)) {
|
|
6296
6834
|
const gqlPermission = types[typeName].permissions.gql;
|
|
6297
6835
|
if (!gqlPermission) continue;
|
|
6836
|
+
const desiredPermission = protoGqlPermission(gqlPermission);
|
|
6837
|
+
const existingPermission = existingGqlPermissions.find((entry) => entry.typeName === typeName);
|
|
6298
6838
|
if (existingNameSet.has(typeName)) {
|
|
6299
|
-
changeSet.
|
|
6839
|
+
if (!forceApplyAll && existingPermission && areNormalizedEqual(normalizeComparableGqlPermission(existingPermission.permission), normalizeComparableGqlPermission(desiredPermission))) changeSet.unchanged.push({ name: typeName });
|
|
6840
|
+
else changeSet.updates.push({
|
|
6300
6841
|
name: typeName,
|
|
6301
6842
|
request: {
|
|
6302
6843
|
workspaceId,
|
|
6303
6844
|
namespaceName: tailordb.namespace,
|
|
6304
6845
|
typeName,
|
|
6305
|
-
permission:
|
|
6846
|
+
permission: desiredPermission
|
|
6306
6847
|
}
|
|
6307
6848
|
});
|
|
6308
6849
|
existingNameSet.delete(typeName);
|
|
@@ -6312,7 +6853,7 @@ async function planGqlPermissions(client, workspaceId, tailordbs, deletedService
|
|
|
6312
6853
|
workspaceId,
|
|
6313
6854
|
namespaceName: tailordb.namespace,
|
|
6314
6855
|
typeName,
|
|
6315
|
-
permission:
|
|
6856
|
+
permission: desiredPermission
|
|
6316
6857
|
}
|
|
6317
6858
|
});
|
|
6318
6859
|
}
|
|
@@ -6339,6 +6880,12 @@ async function planGqlPermissions(client, workspaceId, tailordbs, deletedService
|
|
|
6339
6880
|
});
|
|
6340
6881
|
return changeSet;
|
|
6341
6882
|
}
|
|
6883
|
+
function normalizeComparableGqlPermission(permission) {
|
|
6884
|
+
return { policies: (normalizeProtoConfig(permission)?.policies ?? []).map((policy) => ({
|
|
6885
|
+
...policy,
|
|
6886
|
+
actions: [...policy.actions ?? []].sort((left, right) => left - right)
|
|
6887
|
+
})) };
|
|
6888
|
+
}
|
|
6342
6889
|
function protoGqlPermission(permission) {
|
|
6343
6890
|
return { policies: permission.map((policy) => protoGqlPolicy(policy)) };
|
|
6344
6891
|
}
|
|
@@ -6503,7 +7050,7 @@ function formatMigrationCheckResults(results) {
|
|
|
6503
7050
|
async function applyWorkflow(client, result, phase = "create-update") {
|
|
6504
7051
|
const { changeSet, appName } = result;
|
|
6505
7052
|
if (phase === "create-update") {
|
|
6506
|
-
const jobFunctionVersions = await registerJobFunctions(client, changeSet, appName);
|
|
7053
|
+
const jobFunctionVersions = await registerJobFunctions(client, changeSet, appName, result.unchangedWorkflowJobNames);
|
|
6507
7054
|
await Promise.all([...changeSet.creates.map(async (create) => {
|
|
6508
7055
|
const filteredVersions = filterJobFunctionVersions(jobFunctionVersions, create.usedJobNames);
|
|
6509
7056
|
await client.createWorkflow({
|
|
@@ -6549,14 +7096,16 @@ function filterJobFunctionVersions(allVersions, usedJobNames) {
|
|
|
6549
7096
|
* @param client - Operator client instance
|
|
6550
7097
|
* @param changeSet - Workflow change set
|
|
6551
7098
|
* @param appName - Application name
|
|
7099
|
+
* @param unchangedWorkflowJobNames - Job function names used by unchanged workflows
|
|
6552
7100
|
* @returns Map of job function names to versions
|
|
6553
7101
|
*/
|
|
6554
|
-
async function registerJobFunctions(client, changeSet, appName) {
|
|
7102
|
+
async function registerJobFunctions(client, changeSet, appName, unchangedWorkflowJobNames = /* @__PURE__ */ new Set()) {
|
|
6555
7103
|
const jobFunctionVersions = {};
|
|
6556
|
-
const firstWorkflow = changeSet.creates[0] || changeSet.updates[0];
|
|
7104
|
+
const firstWorkflow = changeSet.creates[0] || changeSet.updates[0] || changeSet.deletes[0];
|
|
6557
7105
|
if (!firstWorkflow) return jobFunctionVersions;
|
|
6558
7106
|
const { workspaceId } = firstWorkflow;
|
|
6559
7107
|
const allUsedJobNames = /* @__PURE__ */ new Set();
|
|
7108
|
+
unchangedWorkflowJobNames.forEach((jobName) => allUsedJobNames.add(jobName));
|
|
6560
7109
|
for (const item of [...changeSet.creates, ...changeSet.updates]) for (const jobName of item.usedJobNames) allUsedJobNames.add(jobName);
|
|
6561
7110
|
const existingJobFunctions = await fetchAll(async (pageToken, maxPageSize) => {
|
|
6562
7111
|
const response = await client.listWorkflowJobFunctions({
|
|
@@ -6567,23 +7116,25 @@ async function registerJobFunctions(client, changeSet, appName) {
|
|
|
6567
7116
|
return [response.jobFunctions.map((j) => j.name), response.nextPageToken];
|
|
6568
7117
|
});
|
|
6569
7118
|
const existingJobNamesSet = new Set(existingJobFunctions);
|
|
6570
|
-
|
|
6571
|
-
const
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
7119
|
+
if (changeSet.creates.length > 0 || changeSet.updates.length > 0) {
|
|
7120
|
+
const results = await Promise.all(Array.from(allUsedJobNames).map(async (jobName) => {
|
|
7121
|
+
const response = existingJobNamesSet.has(jobName) ? await client.updateWorkflowJobFunction({
|
|
7122
|
+
workspaceId,
|
|
7123
|
+
jobFunctionName: jobName,
|
|
7124
|
+
scriptRef: workflowJobFunctionName(jobName)
|
|
7125
|
+
}) : await client.createWorkflowJobFunction({
|
|
7126
|
+
workspaceId,
|
|
7127
|
+
jobFunctionName: jobName,
|
|
7128
|
+
scriptRef: workflowJobFunctionName(jobName)
|
|
7129
|
+
});
|
|
7130
|
+
await client.setMetadata(await buildMetaRequest(jobFunctionTrn(workspaceId, jobName), appName));
|
|
7131
|
+
return {
|
|
7132
|
+
jobName,
|
|
7133
|
+
version: response.jobFunction?.version
|
|
7134
|
+
};
|
|
7135
|
+
}));
|
|
7136
|
+
for (const { jobName, version } of results) if (version) jobFunctionVersions[jobName] = version;
|
|
7137
|
+
}
|
|
6587
7138
|
const unusedJobFunctions = existingJobFunctions.filter((jobName) => !allUsedJobNames.has(jobName));
|
|
6588
7139
|
await Promise.all(unusedJobFunctions.map(async (jobName) => {
|
|
6589
7140
|
const { metadata } = await client.getMetadata({ trn: jobFunctionTrn(workspaceId, jobName) });
|
|
@@ -6611,7 +7162,7 @@ function toRetryPolicy(policy) {
|
|
|
6611
7162
|
backoffMultiplier: policy.backoffMultiplier
|
|
6612
7163
|
};
|
|
6613
7164
|
}
|
|
6614
|
-
function workflowTrn(workspaceId, name) {
|
|
7165
|
+
function workflowTrn$1(workspaceId, name) {
|
|
6615
7166
|
return `trn:v1:workspace:${workspaceId}:workflow:${name}`;
|
|
6616
7167
|
}
|
|
6617
7168
|
function jobFunctionTrn(workspaceId, name) {
|
|
@@ -6624,35 +7175,35 @@ function jobFunctionTrn(workspaceId, name) {
|
|
|
6624
7175
|
* @param appName - Application name
|
|
6625
7176
|
* @param workflows - Parsed workflows
|
|
6626
7177
|
* @param mainJobDeps - Main job dependencies by workflow
|
|
7178
|
+
* @param unchangedJobFunctions - Job functions already proven unchanged by function registry plan
|
|
6627
7179
|
* @returns Planned workflow changes
|
|
6628
7180
|
*/
|
|
6629
|
-
async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps) {
|
|
7181
|
+
async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps, unchangedJobFunctions = /* @__PURE__ */ new Set()) {
|
|
6630
7182
|
const changeSet = createChangeSet("Workflows");
|
|
6631
7183
|
const conflicts = [];
|
|
6632
7184
|
const unmanaged = [];
|
|
6633
7185
|
const resourceOwners = /* @__PURE__ */ new Set();
|
|
7186
|
+
const unchangedWorkflowJobNames = /* @__PURE__ */ new Set();
|
|
6634
7187
|
const withoutLabel = await fetchAll(async (pageToken, maxPageSize) => {
|
|
6635
7188
|
const response = await client.listWorkflows({
|
|
6636
7189
|
workspaceId,
|
|
6637
7190
|
pageToken,
|
|
6638
7191
|
pageSize: maxPageSize
|
|
6639
7192
|
});
|
|
6640
|
-
return [response.workflows.
|
|
6641
|
-
id: w.id,
|
|
6642
|
-
name: w.name
|
|
6643
|
-
})), response.nextPageToken];
|
|
7193
|
+
return [response.workflows, response.nextPageToken];
|
|
6644
7194
|
});
|
|
6645
7195
|
const existingWorkflows = {};
|
|
6646
7196
|
await Promise.all(withoutLabel.map(async (resource) => {
|
|
6647
|
-
const { metadata } = await client.getMetadata({ trn: workflowTrn(workspaceId, resource.name) });
|
|
7197
|
+
const { metadata } = await client.getMetadata({ trn: workflowTrn$1(workspaceId, resource.name) });
|
|
6648
7198
|
existingWorkflows[resource.name] = {
|
|
6649
7199
|
resource,
|
|
6650
|
-
label: metadata?.labels[sdkNameLabelKey]
|
|
7200
|
+
label: metadata?.labels[sdkNameLabelKey],
|
|
7201
|
+
allLabels: metadata?.labels
|
|
6651
7202
|
};
|
|
6652
7203
|
}));
|
|
6653
7204
|
for (const workflow of Object.values(workflows)) {
|
|
6654
7205
|
const existing = existingWorkflows[workflow.name];
|
|
6655
|
-
const metaRequest = await buildMetaRequest(workflowTrn(workspaceId, workflow.name), appName);
|
|
7206
|
+
const metaRequest = await buildMetaRequest(workflowTrn$1(workspaceId, workflow.name), appName);
|
|
6656
7207
|
const usedJobNames = mainJobDeps[workflow.mainJob.name];
|
|
6657
7208
|
if (!usedJobNames) throw new Error(`Job "${workflow.mainJob.name}" (mainJob of workflow "${workflow.name}") was not found.\n\nPossible causes:\n - The job is not exported as a named export\n - The file containing the job is not included in workflow.files glob pattern\n\nSolution:\n export const ${workflow.mainJob.name} = createWorkflowJob({ name: "${workflow.mainJob.name}", ... })`);
|
|
6658
7209
|
if (existing) {
|
|
@@ -6665,7 +7216,10 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
|
|
|
6665
7216
|
resourceName: workflow.name,
|
|
6666
7217
|
currentOwner: existing.label
|
|
6667
7218
|
});
|
|
6668
|
-
|
|
7219
|
+
if (existing.label === appName && hasMatchingSdkVersion(existing.allLabels, metaRequest.labels) && canTreatWorkflowAsUnchanged(existing.resource, workflow, usedJobNames, unchangedJobFunctions)) {
|
|
7220
|
+
changeSet.unchanged.push({ name: workflow.name });
|
|
7221
|
+
for (const jobName of usedJobNames) unchangedWorkflowJobNames.add(jobName);
|
|
7222
|
+
} else changeSet.updates.push({
|
|
6669
7223
|
name: workflow.name,
|
|
6670
7224
|
workspaceId,
|
|
6671
7225
|
workflow,
|
|
@@ -6696,12 +7250,158 @@ async function planWorkflow(client, workspaceId, appName, workflows, mainJobDeps
|
|
|
6696
7250
|
conflicts,
|
|
6697
7251
|
unmanaged,
|
|
6698
7252
|
resourceOwners,
|
|
6699
|
-
appName
|
|
7253
|
+
appName,
|
|
7254
|
+
unchangedWorkflowJobNames
|
|
7255
|
+
};
|
|
7256
|
+
}
|
|
7257
|
+
function canTreatWorkflowAsUnchanged(existing, workflow, usedJobNames, unchangedJobFunctions) {
|
|
7258
|
+
if (!usedJobNames.every((jobName) => unchangedJobFunctions.has(jobName))) return false;
|
|
7259
|
+
return areWorkflowsEqual(existing, workflow, usedJobNames);
|
|
7260
|
+
}
|
|
7261
|
+
function areWorkflowsEqual(existing, workflow, usedJobNames) {
|
|
7262
|
+
return existing.mainJobFunctionName === workflow.mainJob.name && areNormalizedEqual(normalizeComparableExistingWorkflowRetryPolicy(existing.retryPolicy), normalizeComparableWorkflowRetryPolicy(workflow.retryPolicy)) && areNormalizedEqual(normalizeComparableWorkflowJobNames(existing.jobFunctions), normalizeComparableWorkflowJobNames(usedJobNames));
|
|
7263
|
+
}
|
|
7264
|
+
function normalizeComparableExistingWorkflowRetryPolicy(policy) {
|
|
7265
|
+
if (!policy) return;
|
|
7266
|
+
return normalizeRetryPolicyForCompare({
|
|
7267
|
+
maxRetries: policy.maxRetries ?? 0,
|
|
7268
|
+
backoffMultiplier: policy.backoffMultiplier ?? 0,
|
|
7269
|
+
initialBackoff: {
|
|
7270
|
+
seconds: policy.initialBackoff?.seconds ?? 0n,
|
|
7271
|
+
nanos: policy.initialBackoff?.nanos ?? 0
|
|
7272
|
+
},
|
|
7273
|
+
maxBackoff: {
|
|
7274
|
+
seconds: policy.maxBackoff?.seconds ?? 0n,
|
|
7275
|
+
nanos: policy.maxBackoff?.nanos ?? 0
|
|
7276
|
+
}
|
|
7277
|
+
});
|
|
7278
|
+
}
|
|
7279
|
+
function normalizeComparableWorkflowRetryPolicy(policy) {
|
|
7280
|
+
if (!policy) return;
|
|
7281
|
+
return normalizeRetryPolicyForCompare({
|
|
7282
|
+
maxRetries: policy.maxRetries,
|
|
7283
|
+
backoffMultiplier: policy.backoffMultiplier,
|
|
7284
|
+
initialBackoff: parseDurationToProto(policy.initialBackoff),
|
|
7285
|
+
maxBackoff: parseDurationToProto(policy.maxBackoff)
|
|
7286
|
+
});
|
|
7287
|
+
}
|
|
7288
|
+
function normalizeComparableWorkflowJobNames(jobFunctions) {
|
|
7289
|
+
return Array.isArray(jobFunctions) ? [...jobFunctions].sort() : Object.keys(jobFunctions ?? {}).sort();
|
|
7290
|
+
}
|
|
7291
|
+
function normalizeRetryPolicyForCompare(policy) {
|
|
7292
|
+
return {
|
|
7293
|
+
maxRetries: policy.maxRetries,
|
|
7294
|
+
backoffMultiplier: policy.backoffMultiplier,
|
|
7295
|
+
initialBackoff: {
|
|
7296
|
+
seconds: String(policy.initialBackoff.seconds),
|
|
7297
|
+
nanos: policy.initialBackoff.nanos
|
|
7298
|
+
},
|
|
7299
|
+
maxBackoff: {
|
|
7300
|
+
seconds: String(policy.maxBackoff.seconds),
|
|
7301
|
+
nanos: policy.maxBackoff.nanos
|
|
7302
|
+
}
|
|
6700
7303
|
};
|
|
6701
7304
|
}
|
|
6702
7305
|
|
|
6703
7306
|
//#endregion
|
|
6704
7307
|
//#region src/cli/commands/apply/apply.ts
|
|
7308
|
+
function applicationTrn(workspaceId, name) {
|
|
7309
|
+
return `trn:v1:workspace:${workspaceId}:application:${name}`;
|
|
7310
|
+
}
|
|
7311
|
+
function functionRegistryTrn(workspaceId, name) {
|
|
7312
|
+
return `trn:v1:workspace:${workspaceId}:function_registry:${name}`;
|
|
7313
|
+
}
|
|
7314
|
+
function pipelineTrn(workspaceId, name) {
|
|
7315
|
+
return `trn:v1:workspace:${workspaceId}:pipeline:${name}`;
|
|
7316
|
+
}
|
|
7317
|
+
function idpTrn(workspaceId, name) {
|
|
7318
|
+
return `trn:v1:workspace:${workspaceId}:idp:${name}`;
|
|
7319
|
+
}
|
|
7320
|
+
function authTrn(workspaceId, name) {
|
|
7321
|
+
return `trn:v1:workspace:${workspaceId}:auth:${name}`;
|
|
7322
|
+
}
|
|
7323
|
+
function executorTrn(workspaceId, name) {
|
|
7324
|
+
return `trn:v1:workspace:${workspaceId}:executor:${name}`;
|
|
7325
|
+
}
|
|
7326
|
+
function workflowTrn(workspaceId, name) {
|
|
7327
|
+
return `trn:v1:workspace:${workspaceId}:workflow:${name}`;
|
|
7328
|
+
}
|
|
7329
|
+
function staticWebsiteTrn(workspaceId, name) {
|
|
7330
|
+
return `trn:v1:workspace:${workspaceId}:staticwebsite:${name}`;
|
|
7331
|
+
}
|
|
7332
|
+
function tailorDBTrn(workspaceId, name) {
|
|
7333
|
+
return `trn:v1:workspace:${workspaceId}:tailordb:${name}`;
|
|
7334
|
+
}
|
|
7335
|
+
function vaultTrn(workspaceId, name) {
|
|
7336
|
+
return `trn:v1:workspace:${workspaceId}:vault:${name}`;
|
|
7337
|
+
}
|
|
7338
|
+
async function shouldForceApplyAll(client, workspaceId, application, functionEntries) {
|
|
7339
|
+
const desiredLabels = (await buildMetaRequest(applicationTrn(workspaceId, application.name), application.name)).labels;
|
|
7340
|
+
const candidateTrns = /* @__PURE__ */ new Set();
|
|
7341
|
+
if (application.subgraphs.length > 0) candidateTrns.add(applicationTrn(workspaceId, application.name));
|
|
7342
|
+
application.staticWebsiteServices.forEach((website) => {
|
|
7343
|
+
candidateTrns.add(staticWebsiteTrn(workspaceId, website.name));
|
|
7344
|
+
});
|
|
7345
|
+
application.resolverServices.forEach((pipeline) => {
|
|
7346
|
+
candidateTrns.add(pipelineTrn(workspaceId, pipeline.namespace));
|
|
7347
|
+
});
|
|
7348
|
+
application.idpServices.forEach((idp) => {
|
|
7349
|
+
candidateTrns.add(idpTrn(workspaceId, idp.name));
|
|
7350
|
+
});
|
|
7351
|
+
if (application.authService) candidateTrns.add(authTrn(workspaceId, application.authService.config.name));
|
|
7352
|
+
Object.values(application.executorService?.executors ?? {}).forEach((executor) => {
|
|
7353
|
+
candidateTrns.add(executorTrn(workspaceId, executor.name));
|
|
7354
|
+
});
|
|
7355
|
+
Object.values(application.workflowService?.workflows ?? {}).forEach((workflow) => {
|
|
7356
|
+
candidateTrns.add(workflowTrn(workspaceId, workflow.name));
|
|
7357
|
+
});
|
|
7358
|
+
application.tailorDBServices.forEach((service) => {
|
|
7359
|
+
candidateTrns.add(tailorDBTrn(workspaceId, service.namespace));
|
|
7360
|
+
});
|
|
7361
|
+
application.secrets.forEach((vault) => {
|
|
7362
|
+
candidateTrns.add(vaultTrn(workspaceId, vault.vaultName));
|
|
7363
|
+
});
|
|
7364
|
+
functionEntries.forEach((entry) => {
|
|
7365
|
+
candidateTrns.add(functionRegistryTrn(workspaceId, entry.name));
|
|
7366
|
+
});
|
|
7367
|
+
for (const trn of candidateTrns) try {
|
|
7368
|
+
const { metadata } = await client.getMetadata({ trn });
|
|
7369
|
+
if (metadata?.labels?.["sdk-name"] !== application.name) continue;
|
|
7370
|
+
if (!hasMatchingSdkVersion(metadata.labels, desiredLabels)) return true;
|
|
7371
|
+
} catch (error) {
|
|
7372
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) continue;
|
|
7373
|
+
throw error;
|
|
7374
|
+
}
|
|
7375
|
+
return false;
|
|
7376
|
+
}
|
|
7377
|
+
function printPlanSummary(results) {
|
|
7378
|
+
const summary = summarizeChangeSets([
|
|
7379
|
+
results.functionRegistry.changeSet,
|
|
7380
|
+
results.tailorDB.changeSet.service,
|
|
7381
|
+
results.tailorDB.changeSet.type,
|
|
7382
|
+
results.tailorDB.changeSet.gqlPermission,
|
|
7383
|
+
results.staticWebsite.changeSet,
|
|
7384
|
+
results.idp.changeSet.service,
|
|
7385
|
+
results.idp.changeSet.client,
|
|
7386
|
+
results.auth.changeSet.service,
|
|
7387
|
+
results.auth.changeSet.idpConfig,
|
|
7388
|
+
results.auth.changeSet.userProfileConfig,
|
|
7389
|
+
results.auth.changeSet.tenantConfig,
|
|
7390
|
+
results.auth.changeSet.machineUser,
|
|
7391
|
+
results.auth.changeSet.oauth2Client,
|
|
7392
|
+
results.auth.changeSet.authHook,
|
|
7393
|
+
results.auth.changeSet.scim,
|
|
7394
|
+
results.auth.changeSet.scimResource,
|
|
7395
|
+
results.pipeline.changeSet.service,
|
|
7396
|
+
results.pipeline.changeSet.resolver,
|
|
7397
|
+
results.app,
|
|
7398
|
+
results.executor.changeSet,
|
|
7399
|
+
results.workflow.changeSet,
|
|
7400
|
+
results.secretManager.vaultChangeSet,
|
|
7401
|
+
results.secretManager.secretChangeSet
|
|
7402
|
+
]);
|
|
7403
|
+
logger.log(formatPlanSummary(summary));
|
|
7404
|
+
}
|
|
6705
7405
|
/**
|
|
6706
7406
|
* Apply the configured application to the Tailor platform.
|
|
6707
7407
|
* @param options - Options for apply execution
|
|
@@ -6775,9 +7475,10 @@ async function apply(options) {
|
|
|
6775
7475
|
rootSpan.setAttribute("app.name", application.name);
|
|
6776
7476
|
rootSpan.setAttribute("workspace.id", workspaceId);
|
|
6777
7477
|
const workflowService = application.workflowService;
|
|
6778
|
-
const functionEntries = collectFunctionEntries(application, workflowService?.jobs ?? [], bundledScripts);
|
|
7478
|
+
const functionEntries = collectFunctionEntries(application, filterBundledWorkflowJobs(workflowService?.jobs ?? [], workflowBuildResult?.usedJobNames ?? []), bundledScripts);
|
|
6779
7479
|
const dryRun = options?.dryRun ?? false;
|
|
6780
7480
|
const yes = options?.yes ?? false;
|
|
7481
|
+
const forceApplyAll = await withSpan("plan.detectSdkVersionChange", () => shouldForceApplyAll(client, workspaceId, application, functionEntries));
|
|
6781
7482
|
const { functionRegistry, tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow, secretManager } = await withSpan("plan", async () => {
|
|
6782
7483
|
const ctx = {
|
|
6783
7484
|
client,
|
|
@@ -6785,10 +7486,12 @@ async function apply(options) {
|
|
|
6785
7486
|
application,
|
|
6786
7487
|
forRemoval: false,
|
|
6787
7488
|
config,
|
|
6788
|
-
noSchemaCheck: options?.noSchemaCheck
|
|
7489
|
+
noSchemaCheck: options?.noSchemaCheck,
|
|
7490
|
+
forceApplyAll
|
|
6789
7491
|
};
|
|
6790
|
-
const
|
|
6791
|
-
|
|
7492
|
+
const functionRegistry = await withSpan("plan.functionRegistry", () => planFunctionRegistry(client, workspaceId, application.name, functionEntries));
|
|
7493
|
+
const unchangedWorkflowJobs = new Set(functionRegistry.changeSet.unchanged.map((entry) => entry.name).filter((name) => name.startsWith("workflow--")).map((name) => name.slice(10)));
|
|
7494
|
+
const [tailorDB, staticWebsite, idp, auth, pipeline, app, executor, workflow, secretManager] = await Promise.all([
|
|
6792
7495
|
withSpan("plan.tailorDB", () => planTailorDB(ctx)),
|
|
6793
7496
|
withSpan("plan.staticWebsite", () => planStaticWebsite(ctx)),
|
|
6794
7497
|
withSpan("plan.idp", () => planIdP(ctx)),
|
|
@@ -6796,7 +7499,7 @@ async function apply(options) {
|
|
|
6796
7499
|
withSpan("plan.pipeline", () => planPipeline(ctx)),
|
|
6797
7500
|
withSpan("plan.application", () => planApplication(ctx)),
|
|
6798
7501
|
withSpan("plan.executor", () => planExecutor(ctx)),
|
|
6799
|
-
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {})),
|
|
7502
|
+
withSpan("plan.workflow", () => planWorkflow(client, workspaceId, application.name, workflowService?.workflows ?? {}, workflowBuildResult?.mainJobDeps ?? {}, unchangedWorkflowJobs)),
|
|
6800
7503
|
withSpan("plan.secretManager", () => planSecretManager(ctx))
|
|
6801
7504
|
]);
|
|
6802
7505
|
return {
|
|
@@ -6882,6 +7585,18 @@ async function apply(options) {
|
|
|
6882
7585
|
}
|
|
6883
7586
|
});
|
|
6884
7587
|
});
|
|
7588
|
+
printPlanSummary({
|
|
7589
|
+
functionRegistry,
|
|
7590
|
+
tailorDB,
|
|
7591
|
+
staticWebsite,
|
|
7592
|
+
idp,
|
|
7593
|
+
auth,
|
|
7594
|
+
pipeline,
|
|
7595
|
+
app,
|
|
7596
|
+
executor,
|
|
7597
|
+
workflow,
|
|
7598
|
+
secretManager
|
|
7599
|
+
});
|
|
6885
7600
|
if (dryRun) {
|
|
6886
7601
|
logger.info("Dry run enabled. No changes applied.");
|
|
6887
7602
|
return;
|
|
@@ -7214,7 +7929,7 @@ async function getExecutor(options) {
|
|
|
7214
7929
|
try {
|
|
7215
7930
|
return toExecutorInfo(await resolveExecutor(client, workspaceId, name));
|
|
7216
7931
|
} catch (error) {
|
|
7217
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${name}' not found
|
|
7932
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${name}' not found.`, { cause: error });
|
|
7218
7933
|
throw error;
|
|
7219
7934
|
}
|
|
7220
7935
|
}
|
|
@@ -7690,7 +8405,7 @@ async function getWorkflow(options) {
|
|
|
7690
8405
|
try {
|
|
7691
8406
|
return toWorkflowInfo(await resolveWorkflow(client, workspaceId, name));
|
|
7692
8407
|
} catch (error) {
|
|
7693
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Workflow '${name}' not found
|
|
8408
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Workflow '${name}' not found.`, { cause: error });
|
|
7694
8409
|
throw error;
|
|
7695
8410
|
}
|
|
7696
8411
|
}
|
|
@@ -7821,7 +8536,7 @@ async function startWorkflowCore(options) {
|
|
|
7821
8536
|
})
|
|
7822
8537
|
};
|
|
7823
8538
|
} catch (error) {
|
|
7824
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Workflow '${workflowName}' not found
|
|
8539
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Workflow '${workflowName}' not found.`, { cause: error });
|
|
7825
8540
|
throw error;
|
|
7826
8541
|
}
|
|
7827
8542
|
}
|
|
@@ -7949,7 +8664,7 @@ async function listExecutorJobs(options) {
|
|
|
7949
8664
|
});
|
|
7950
8665
|
return jobs.map(toExecutorJobListInfo);
|
|
7951
8666
|
} catch (error) {
|
|
7952
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${executorName}' not found
|
|
8667
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${executorName}' not found.`, { cause: error });
|
|
7953
8668
|
throw error;
|
|
7954
8669
|
}
|
|
7955
8670
|
}
|
|
@@ -7989,7 +8704,7 @@ async function getExecutorJob(options) {
|
|
|
7989
8704
|
}
|
|
7990
8705
|
return jobInfo;
|
|
7991
8706
|
} catch (error) {
|
|
7992
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Job '${options.jobId}' not found for executor '${executorName}'
|
|
8707
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Job '${options.jobId}' not found for executor '${executorName}'.`, { cause: error });
|
|
7993
8708
|
throw error;
|
|
7994
8709
|
}
|
|
7995
8710
|
}
|
|
@@ -8378,8 +9093,8 @@ async function triggerExecutorByName(options) {
|
|
|
8378
9093
|
payload: options.payload
|
|
8379
9094
|
})).jobId };
|
|
8380
9095
|
} catch (error) {
|
|
8381
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${options.executorName}' not found
|
|
8382
|
-
if (error instanceof ConnectError && error.code === Code.InvalidArgument) throw new Error(`Invalid argument: ${error.message}
|
|
9096
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`Executor '${options.executorName}' not found.`, { cause: error });
|
|
9097
|
+
if (error instanceof ConnectError && error.code === Code.InvalidArgument) throw new Error(`Invalid argument: ${error.message}`, { cause: error });
|
|
8383
9098
|
throw error;
|
|
8384
9099
|
}
|
|
8385
9100
|
}
|
|
@@ -9665,7 +10380,7 @@ async function getOAuth2Client(options) {
|
|
|
9665
10380
|
});
|
|
9666
10381
|
return toOAuth2ClientCredentials(oauth2Client);
|
|
9667
10382
|
} catch (error) {
|
|
9668
|
-
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`OAuth2 client '${options.name}' not found
|
|
10383
|
+
if (error instanceof ConnectError && error.code === Code.NotFound) throw new Error(`OAuth2 client '${options.name}' not found.`, { cause: error });
|
|
9669
10384
|
throw error;
|
|
9670
10385
|
}
|
|
9671
10386
|
}
|
|
@@ -10965,7 +11680,7 @@ async function generate(options) {
|
|
|
10965
11680
|
if (options.init) await handleInitOption(namespacesWithMigrations, options.yes);
|
|
10966
11681
|
let pluginManager;
|
|
10967
11682
|
if (plugins.length > 0) pluginManager = new PluginManager(plugins);
|
|
10968
|
-
const { defineApplication } = await import("./application-
|
|
11683
|
+
const { defineApplication } = await import("./application-ChVyhwe-.mjs");
|
|
10969
11684
|
const application = defineApplication({
|
|
10970
11685
|
config,
|
|
10971
11686
|
pluginManager
|
|
@@ -11383,8 +12098,8 @@ async function resumeWorkflow(options) {
|
|
|
11383
12098
|
};
|
|
11384
12099
|
} catch (error) {
|
|
11385
12100
|
if (error instanceof ConnectError) {
|
|
11386
|
-
if (error.code === Code.NotFound) throw new Error(`Execution '${options.executionId}' not found
|
|
11387
|
-
if (error.code === Code.FailedPrecondition) throw new Error(`Execution '${options.executionId}' is not in a resumable state
|
|
12101
|
+
if (error.code === Code.NotFound) throw new Error(`Execution '${options.executionId}' not found.`, { cause: error });
|
|
12102
|
+
if (error.code === Code.FailedPrecondition) throw new Error(`Execution '${options.executionId}' is not in a resumable state.`, { cause: error });
|
|
11388
12103
|
}
|
|
11389
12104
|
throw error;
|
|
11390
12105
|
}
|
|
@@ -12476,7 +13191,7 @@ function extractTypeNamesFromSql(query) {
|
|
|
12476
13191
|
statements = parse(query);
|
|
12477
13192
|
} catch (error) {
|
|
12478
13193
|
const message = error instanceof Error ? error.message : String(error);
|
|
12479
|
-
throw new Error(`SQL parse error: ${message}\nIf your table name is a reserved keyword (e.g. User), wrap it in double quotes: SELECT * FROM "User"
|
|
13194
|
+
throw new Error(`SQL parse error: ${message}\nIf your table name is a reserved keyword (e.g. User), wrap it in double quotes: SELECT * FROM "User"`, { cause: error });
|
|
12480
13195
|
}
|
|
12481
13196
|
const typeNames = /* @__PURE__ */ new Set();
|
|
12482
13197
|
const visitor = astVisitor((mapper) => ({ tableRef: (tableRef) => {
|
|
@@ -12714,7 +13429,7 @@ async function resolveEditedQueryInput(engine) {
|
|
|
12714
13429
|
try {
|
|
12715
13430
|
await openInEditor(filePath, editor);
|
|
12716
13431
|
} catch (error) {
|
|
12717
|
-
throw new Error(`Failed to open query editor "${editor}": ${error instanceof Error ? error.message : String(error)}
|
|
13432
|
+
throw new Error(`Failed to open query editor "${editor}": ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
12718
13433
|
}
|
|
12719
13434
|
const editedQuery = await fs.readFile(filePath, "utf-8");
|
|
12720
13435
|
if (editedQuery.trim().length === 0 || editedQuery === initialQuery) return { mode: "abort" };
|
|
@@ -13170,4 +13885,4 @@ function isDeno() {
|
|
|
13170
13885
|
|
|
13171
13886
|
//#endregion
|
|
13172
13887
|
export { getFolder as $, getNextMigrationNumber as $t, listWorkflows as A, functionExecutionStatusToString as At, updateCommand$1 as B, DB_TYPES_FILE_NAME as Bt, listApps as C, startCommand as Ct, resumeCommand as D, executionsCommand as Dt, healthCommand as E, getWorkflow as Et, show as F, executeScript as Ft, listOrganizations as G, compareLocalTypesWithSnapshot as Gt, organizationTree as H, INITIAL_SCHEMA_NUMBER as Ht, showCommand as I, waitForExecution$1 as It, updateCommand$2 as J, formatMigrationNumber as Jt, getCommand$1 as K, compareSnapshots as Kt, logBetaWarning as L, MIGRATION_LABEL_KEY as Lt, truncateCommand as M, getCommand$5 as Mt, generate as N, getExecutor as Nt, resumeWorkflow as O, getWorkflowExecution as Ot, generateCommand as P, apply as Pt, getCommand$2 as Q, getMigrationFiles as Qt, remove as R, parseMigrationLabelNumber as Rt, createWorkspace as S, watchExecutorJob as St, getAppHealth as T, getCommand$4 as Tt, treeCommand as U, MIGRATE_FILE_NAME as Ut, updateOrganization as V, DIFF_FILE_NAME as Vt, listCommand$4 as W, SCHEMA_FILE_NAME as Wt, listCommand$5 as X, getMigrationDirPath as Xt, updateFolder as Y, getLatestMigrationNumber as Yt, listFolders as Z, getMigrationFilePath as Zt, getCommand as _, isVerbose as _n, listCommand$8 as _t, updateCommand as a, hasChanges as an, listOAuth2Clients as at, deleteWorkspace as b, jobsCommand as bt, removeUser as c, sdkNameLabelKey as cn, getMachineUserToken as ct, inviteCommand as d, apiCall as dn, listMachineUsers as dt, isValidMigrationNumber as en, deleteCommand$1 as et, inviteUser as f, apiCommand as fn, generate$1 as ft, listWorkspaces as g, deploymentArgs as gn, triggerExecutor as gt, listCommand$1 as h, confirmationArgs as hn, triggerCommand as ht, isCLIError as i, formatMigrationDiff as in, listCommand$6 as it, truncate as j, formatKeyValueTable as jt, listCommand$3 as k, listWorkflowExecutions as kt, listCommand as l, trnPrefix as ln, tokenCommand as lt, restoreWorkspace as m, commonArgs as mn, webhookCommand as mt, query as n, reconstructSnapshotFromMigrations as nn, createCommand$1 as nt, updateUser as o, getNamespacesWithMigrations as on, getCommand$3 as ot, restoreCommand as p, defineAppCommand as pn, listWebhookExecutors as pt, getOrganization as q, createSnapshotFromLocalTypes as qt, queryCommand as r, formatDiffSummary as rn, createFolder as rt, removeCommand as s, prompt as sn, getOAuth2Client as st, isNativeTypeScriptRuntime as t, loadDiff as tn, deleteFolder as tt, listUsers as u, generateUserTypes as un, listCommand$7 as ut, getWorkspace as v, workspaceArgs as vn, listExecutors as vt, listCommand$2 as w, startWorkflow as wt, createCommand as x, listExecutorJobs as xt, deleteCommand as y, getExecutorJob as yt, removeCommand$1 as z, bundleMigrationScript as zt };
|
|
13173
|
-
//# sourceMappingURL=runtime-
|
|
13888
|
+
//# sourceMappingURL=runtime-3P9JFpe9.mjs.map
|