@go-to-k/cdkd 0.63.0 → 0.65.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/dist/cli.js CHANGED
@@ -56180,6 +56180,35 @@ import {
56180
56180
  GetTableCommand,
56181
56181
  GetTablesCommand,
56182
56182
  GetTagsCommand,
56183
+ CreateWorkflowCommand,
56184
+ UpdateWorkflowCommand,
56185
+ DeleteWorkflowCommand,
56186
+ GetWorkflowCommand,
56187
+ ListWorkflowsCommand,
56188
+ CreateSecurityConfigurationCommand,
56189
+ DeleteSecurityConfigurationCommand,
56190
+ GetSecurityConfigurationCommand,
56191
+ GetSecurityConfigurationsCommand,
56192
+ CreateJobCommand,
56193
+ UpdateJobCommand,
56194
+ DeleteJobCommand,
56195
+ GetJobCommand,
56196
+ CreateCrawlerCommand,
56197
+ UpdateCrawlerCommand,
56198
+ DeleteCrawlerCommand,
56199
+ GetCrawlerCommand,
56200
+ StartCrawlerScheduleCommand,
56201
+ StopCrawlerScheduleCommand,
56202
+ CreateConnectionCommand,
56203
+ UpdateConnectionCommand,
56204
+ DeleteConnectionCommand,
56205
+ GetConnectionCommand,
56206
+ CreateTriggerCommand,
56207
+ UpdateTriggerCommand,
56208
+ DeleteTriggerCommand,
56209
+ GetTriggerCommand,
56210
+ StartTriggerCommand,
56211
+ StopTriggerCommand,
56183
56212
  EntityNotFoundException
56184
56213
  } from "@aws-sdk/client-glue";
56185
56214
  import { STSClient as STSClient8, GetCallerIdentityCommand as GetCallerIdentityCommand8 } from "@aws-sdk/client-sts";
@@ -56872,6 +56901,1588 @@ var GlueProvider = class {
56872
56901
  return this.cachedAccountId;
56873
56902
  }
56874
56903
  };
56904
+ var GlueWorkflowProvider = class {
56905
+ client;
56906
+ stsClient;
56907
+ cachedAccountId;
56908
+ providerRegion = process.env["AWS_REGION"];
56909
+ logger = getLogger().child("GlueWorkflowProvider");
56910
+ handledProperties = /* @__PURE__ */ new Map([
56911
+ [
56912
+ "AWS::Glue::Workflow",
56913
+ /* @__PURE__ */ new Set(["Name", "Description", "DefaultRunProperties", "MaxConcurrentRuns", "Tags"])
56914
+ ]
56915
+ ]);
56916
+ getClient() {
56917
+ if (!this.client) {
56918
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
56919
+ }
56920
+ return this.client;
56921
+ }
56922
+ async create(logicalId, resourceType, properties) {
56923
+ this.logger.debug(`Creating Glue Workflow ${logicalId}`);
56924
+ const name = properties["Name"];
56925
+ if (!name) {
56926
+ throw new ProvisioningError(
56927
+ `Name is required for Glue Workflow ${logicalId}`,
56928
+ resourceType,
56929
+ logicalId
56930
+ );
56931
+ }
56932
+ try {
56933
+ const tags = workflowTagsForCreate(properties["Tags"]);
56934
+ await this.getClient().send(
56935
+ new CreateWorkflowCommand({
56936
+ Name: name,
56937
+ ...properties["Description"] !== void 0 && {
56938
+ Description: properties["Description"]
56939
+ },
56940
+ ...properties["DefaultRunProperties"] !== void 0 && {
56941
+ DefaultRunProperties: properties["DefaultRunProperties"]
56942
+ },
56943
+ ...properties["MaxConcurrentRuns"] !== void 0 && {
56944
+ MaxConcurrentRuns: properties["MaxConcurrentRuns"]
56945
+ },
56946
+ ...tags && { Tags: tags }
56947
+ })
56948
+ );
56949
+ this.logger.debug(`Successfully created Glue Workflow ${logicalId}: ${name}`);
56950
+ return { physicalId: name, attributes: {} };
56951
+ } catch (error) {
56952
+ const cause = error instanceof Error ? error : void 0;
56953
+ throw new ProvisioningError(
56954
+ `Failed to create Glue Workflow ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
56955
+ resourceType,
56956
+ logicalId,
56957
+ void 0,
56958
+ cause
56959
+ );
56960
+ }
56961
+ }
56962
+ async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
56963
+ this.logger.debug(`Updating Glue Workflow ${logicalId}: ${physicalId}`);
56964
+ try {
56965
+ await this.getClient().send(
56966
+ new UpdateWorkflowCommand({
56967
+ Name: physicalId,
56968
+ ...properties["Description"] !== void 0 && {
56969
+ Description: properties["Description"]
56970
+ },
56971
+ ...properties["DefaultRunProperties"] !== void 0 && {
56972
+ DefaultRunProperties: properties["DefaultRunProperties"]
56973
+ },
56974
+ ...properties["MaxConcurrentRuns"] !== void 0 && {
56975
+ MaxConcurrentRuns: properties["MaxConcurrentRuns"]
56976
+ }
56977
+ })
56978
+ );
56979
+ this.logger.debug(`Successfully updated Glue Workflow ${logicalId}`);
56980
+ return { physicalId, wasReplaced: false };
56981
+ } catch (error) {
56982
+ const cause = error instanceof Error ? error : void 0;
56983
+ throw new ProvisioningError(
56984
+ `Failed to update Glue Workflow ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
56985
+ resourceType,
56986
+ logicalId,
56987
+ physicalId,
56988
+ cause
56989
+ );
56990
+ }
56991
+ }
56992
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
56993
+ this.logger.debug(`Deleting Glue Workflow ${logicalId}: ${physicalId}`);
56994
+ try {
56995
+ await this.getClient().send(new DeleteWorkflowCommand({ Name: physicalId }));
56996
+ this.logger.debug(`Successfully deleted Glue Workflow ${logicalId}`);
56997
+ } catch (error) {
56998
+ if (error instanceof EntityNotFoundException) {
56999
+ const clientRegion = await this.getClient().config.region();
57000
+ assertRegionMatch(
57001
+ clientRegion,
57002
+ context?.expectedRegion,
57003
+ resourceType,
57004
+ logicalId,
57005
+ physicalId
57006
+ );
57007
+ this.logger.debug(`Glue Workflow ${physicalId} does not exist, skipping deletion`);
57008
+ return;
57009
+ }
57010
+ const cause = error instanceof Error ? error : void 0;
57011
+ throw new ProvisioningError(
57012
+ `Failed to delete Glue Workflow ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57013
+ resourceType,
57014
+ logicalId,
57015
+ physicalId,
57016
+ cause
57017
+ );
57018
+ }
57019
+ }
57020
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
57021
+ async getAttribute(physicalId, _resourceType, attributeName) {
57022
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
57023
+ return physicalId;
57024
+ }
57025
+ return void 0;
57026
+ }
57027
+ /**
57028
+ * Read AWS-current Workflow shape via `GetWorkflow`. Surfaces every
57029
+ * user-controllable top-level CFn key with always-emit placeholders
57030
+ * (PR #145):
57031
+ * - `Description` → `?? ''`
57032
+ * - `DefaultRunProperties` → `?? {}`
57033
+ * - `MaxConcurrentRuns` → omitted when AWS reports `undefined` (no
57034
+ * AWS-side default to anchor a placeholder against; cdkd state may
57035
+ * legitimately leave this unset)
57036
+ * - `Tags` → `?? []` (filtered against `aws:cdk:path` and the rest of
57037
+ * the `aws:`-prefixed reserved space)
57038
+ */
57039
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
57040
+ let workflow;
57041
+ try {
57042
+ const resp = await this.getClient().send(
57043
+ new GetWorkflowCommand({ Name: physicalId, IncludeGraph: false })
57044
+ );
57045
+ workflow = resp.Workflow;
57046
+ } catch (err) {
57047
+ if (err instanceof EntityNotFoundException)
57048
+ return void 0;
57049
+ throw err;
57050
+ }
57051
+ if (!workflow)
57052
+ return void 0;
57053
+ const result = {
57054
+ Name: workflow.Name ?? physicalId,
57055
+ Description: workflow.Description ?? "",
57056
+ DefaultRunProperties: workflow.DefaultRunProperties ?? {}
57057
+ };
57058
+ if (workflow.MaxConcurrentRuns !== void 0) {
57059
+ result["MaxConcurrentRuns"] = workflow.MaxConcurrentRuns;
57060
+ }
57061
+ const arn = await this.buildWorkflowArn(physicalId);
57062
+ let tags = [];
57063
+ try {
57064
+ const tagResp = await this.getClient().send(new GetTagsCommand({ ResourceArn: arn }));
57065
+ tags = normalizeAwsTagsToCfn(tagResp.Tags);
57066
+ } catch (err) {
57067
+ this.logger.debug(
57068
+ `GetTags failed for Glue Workflow ${physicalId}: ${err instanceof Error ? err.message : String(err)}`
57069
+ );
57070
+ }
57071
+ result["Tags"] = tags;
57072
+ return result;
57073
+ }
57074
+ async import(input) {
57075
+ const explicitName = input.knownPhysicalId ?? input.properties["Name"];
57076
+ if (explicitName) {
57077
+ try {
57078
+ await this.getClient().send(new GetWorkflowCommand({ Name: explicitName }));
57079
+ return { physicalId: explicitName, attributes: {} };
57080
+ } catch (err) {
57081
+ if (err instanceof EntityNotFoundException)
57082
+ return null;
57083
+ throw err;
57084
+ }
57085
+ }
57086
+ if (!input.cdkPath)
57087
+ return null;
57088
+ let nextToken;
57089
+ do {
57090
+ const list = await this.getClient().send(
57091
+ new ListWorkflowsCommand({ ...nextToken && { NextToken: nextToken } })
57092
+ );
57093
+ for (const name of list.Workflows ?? []) {
57094
+ const arn = await this.buildWorkflowArn(name);
57095
+ if (await this.tagsMatchCdkPath(arn, input.cdkPath)) {
57096
+ return { physicalId: name, attributes: {} };
57097
+ }
57098
+ }
57099
+ nextToken = list.NextToken;
57100
+ } while (nextToken);
57101
+ return null;
57102
+ }
57103
+ async tagsMatchCdkPath(arn, cdkPath) {
57104
+ try {
57105
+ const resp = await this.getClient().send(new GetTagsCommand({ ResourceArn: arn }));
57106
+ return resp.Tags?.[CDK_PATH_TAG] === cdkPath;
57107
+ } catch (err) {
57108
+ if (err instanceof EntityNotFoundException)
57109
+ return false;
57110
+ throw err;
57111
+ }
57112
+ }
57113
+ async buildWorkflowArn(workflowName) {
57114
+ const region = await this.getRegion();
57115
+ const account = await this.getAccountId();
57116
+ return `arn:aws:glue:${region}:${account}:workflow/${workflowName}`;
57117
+ }
57118
+ async getRegion() {
57119
+ const region = await this.getClient().config.region();
57120
+ return region || this.providerRegion || "us-east-1";
57121
+ }
57122
+ async getAccountId() {
57123
+ if (this.cachedAccountId)
57124
+ return this.cachedAccountId;
57125
+ if (!this.stsClient) {
57126
+ this.stsClient = new STSClient8(this.providerRegion ? { region: this.providerRegion } : {});
57127
+ }
57128
+ const identity = await this.stsClient.send(new GetCallerIdentityCommand8({}));
57129
+ if (!identity.Account) {
57130
+ throw new Error("Failed to resolve AWS account id from STS");
57131
+ }
57132
+ this.cachedAccountId = identity.Account;
57133
+ return this.cachedAccountId;
57134
+ }
57135
+ };
57136
+ var GlueSecurityConfigurationProvider = class {
57137
+ client;
57138
+ providerRegion = process.env["AWS_REGION"];
57139
+ logger = getLogger().child("GlueSecurityConfigurationProvider");
57140
+ handledProperties = /* @__PURE__ */ new Map([
57141
+ ["AWS::Glue::SecurityConfiguration", /* @__PURE__ */ new Set(["Name", "EncryptionConfiguration"])]
57142
+ ]);
57143
+ getClient() {
57144
+ if (!this.client) {
57145
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
57146
+ }
57147
+ return this.client;
57148
+ }
57149
+ async create(logicalId, resourceType, properties) {
57150
+ this.logger.debug(`Creating Glue SecurityConfiguration ${logicalId}`);
57151
+ const name = properties["Name"];
57152
+ if (!name) {
57153
+ throw new ProvisioningError(
57154
+ `Name is required for Glue SecurityConfiguration ${logicalId}`,
57155
+ resourceType,
57156
+ logicalId
57157
+ );
57158
+ }
57159
+ const encryptionConfiguration = properties["EncryptionConfiguration"];
57160
+ if (!encryptionConfiguration) {
57161
+ throw new ProvisioningError(
57162
+ `EncryptionConfiguration is required for Glue SecurityConfiguration ${logicalId}`,
57163
+ resourceType,
57164
+ logicalId
57165
+ );
57166
+ }
57167
+ try {
57168
+ await this.getClient().send(
57169
+ new CreateSecurityConfigurationCommand({
57170
+ Name: name,
57171
+ EncryptionConfiguration: buildEncryptionConfiguration(encryptionConfiguration)
57172
+ })
57173
+ );
57174
+ this.logger.debug(`Successfully created Glue SecurityConfiguration ${logicalId}: ${name}`);
57175
+ return { physicalId: name, attributes: {} };
57176
+ } catch (error) {
57177
+ const cause = error instanceof Error ? error : void 0;
57178
+ throw new ProvisioningError(
57179
+ `Failed to create Glue SecurityConfiguration ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57180
+ resourceType,
57181
+ logicalId,
57182
+ void 0,
57183
+ cause
57184
+ );
57185
+ }
57186
+ }
57187
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
57188
+ async update(logicalId, _physicalId, resourceType, _properties, _previousProperties) {
57189
+ throw new ResourceUpdateNotSupportedError(
57190
+ resourceType,
57191
+ logicalId,
57192
+ "AWS::Glue::SecurityConfiguration is immutable; AWS provides no Update API. Use cdkd deploy --replace, or destroy + redeploy with the new EncryptionConfiguration."
57193
+ );
57194
+ }
57195
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
57196
+ this.logger.debug(`Deleting Glue SecurityConfiguration ${logicalId}: ${physicalId}`);
57197
+ try {
57198
+ await this.getClient().send(new DeleteSecurityConfigurationCommand({ Name: physicalId }));
57199
+ this.logger.debug(`Successfully deleted Glue SecurityConfiguration ${logicalId}`);
57200
+ } catch (error) {
57201
+ if (error instanceof EntityNotFoundException) {
57202
+ const clientRegion = await this.getClient().config.region();
57203
+ assertRegionMatch(
57204
+ clientRegion,
57205
+ context?.expectedRegion,
57206
+ resourceType,
57207
+ logicalId,
57208
+ physicalId
57209
+ );
57210
+ this.logger.debug(
57211
+ `Glue SecurityConfiguration ${physicalId} does not exist, skipping deletion`
57212
+ );
57213
+ return;
57214
+ }
57215
+ const cause = error instanceof Error ? error : void 0;
57216
+ throw new ProvisioningError(
57217
+ `Failed to delete Glue SecurityConfiguration ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57218
+ resourceType,
57219
+ logicalId,
57220
+ physicalId,
57221
+ cause
57222
+ );
57223
+ }
57224
+ }
57225
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
57226
+ async getAttribute(physicalId, _resourceType, attributeName) {
57227
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
57228
+ return physicalId;
57229
+ }
57230
+ return void 0;
57231
+ }
57232
+ /**
57233
+ * Read AWS-current SecurityConfiguration shape via
57234
+ * `GetSecurityConfiguration`. Always emits the three CFn-modeled
57235
+ * sub-configs (`S3Encryption: []`, `CloudWatchEncryption: {}`,
57236
+ * `JobBookmarksEncryption: {}`) per PR #145 even when AWS reports
57237
+ * nothing — closes the "console-side encryption enable on a previously
57238
+ * default config" detection gap on the v3 baseline.
57239
+ *
57240
+ * `DataQualityEncryption` is silently dropped: the CFn schema for
57241
+ * `AWS::Glue::SecurityConfiguration` does not model it, so surfacing it
57242
+ * would fire false drift on every clean run for a key cdkd state can
57243
+ * never carry.
57244
+ */
57245
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
57246
+ let cfg;
57247
+ try {
57248
+ const resp = await this.getClient().send(
57249
+ new GetSecurityConfigurationCommand({ Name: physicalId })
57250
+ );
57251
+ cfg = resp.SecurityConfiguration;
57252
+ } catch (err) {
57253
+ if (err instanceof EntityNotFoundException)
57254
+ return void 0;
57255
+ throw err;
57256
+ }
57257
+ if (!cfg)
57258
+ return void 0;
57259
+ return {
57260
+ Name: cfg.Name ?? physicalId,
57261
+ EncryptionConfiguration: mapEncryptionConfigurationToCfn(cfg.EncryptionConfiguration)
57262
+ };
57263
+ }
57264
+ async import(input) {
57265
+ const explicitName = input.knownPhysicalId ?? input.properties["Name"];
57266
+ if (explicitName) {
57267
+ try {
57268
+ await this.getClient().send(new GetSecurityConfigurationCommand({ Name: explicitName }));
57269
+ return { physicalId: explicitName, attributes: {} };
57270
+ } catch (err) {
57271
+ if (err instanceof EntityNotFoundException)
57272
+ return null;
57273
+ throw err;
57274
+ }
57275
+ }
57276
+ if (!explicitName) {
57277
+ let nextToken;
57278
+ do {
57279
+ const list = await this.getClient().send(
57280
+ new GetSecurityConfigurationsCommand({ ...nextToken && { NextToken: nextToken } })
57281
+ );
57282
+ for (const _entry of list.SecurityConfigurations ?? []) {
57283
+ }
57284
+ nextToken = list.NextToken;
57285
+ } while (nextToken);
57286
+ return null;
57287
+ }
57288
+ return null;
57289
+ }
57290
+ };
57291
+ function workflowTagsForCreate(tags) {
57292
+ if (!Array.isArray(tags) || tags.length === 0)
57293
+ return void 0;
57294
+ const out = {};
57295
+ for (const t of tags) {
57296
+ const obj = t;
57297
+ const k = typeof obj["Key"] === "string" ? obj["Key"] : void 0;
57298
+ const v = typeof obj["Value"] === "string" ? obj["Value"] : "";
57299
+ if (!k)
57300
+ continue;
57301
+ out[k] = v;
57302
+ }
57303
+ return Object.keys(out).length > 0 ? out : void 0;
57304
+ }
57305
+ function buildEncryptionConfiguration(input) {
57306
+ const result = {};
57307
+ if (Array.isArray(input["S3Encryption"])) {
57308
+ result.S3Encryption = input["S3Encryption"].map((entry) => {
57309
+ const e = entry;
57310
+ const item = {};
57311
+ if (typeof e["S3EncryptionMode"] === "string") {
57312
+ item.S3EncryptionMode = e["S3EncryptionMode"];
57313
+ }
57314
+ if (typeof e["KmsKeyArn"] === "string") {
57315
+ item.KmsKeyArn = e["KmsKeyArn"];
57316
+ }
57317
+ return item;
57318
+ });
57319
+ }
57320
+ if (input["CloudWatchEncryption"] !== void 0) {
57321
+ const cw = input["CloudWatchEncryption"];
57322
+ const item = {};
57323
+ if (typeof cw["CloudWatchEncryptionMode"] === "string") {
57324
+ item.CloudWatchEncryptionMode = cw["CloudWatchEncryptionMode"];
57325
+ }
57326
+ if (typeof cw["KmsKeyArn"] === "string") {
57327
+ item.KmsKeyArn = cw["KmsKeyArn"];
57328
+ }
57329
+ result.CloudWatchEncryption = item;
57330
+ }
57331
+ if (input["JobBookmarksEncryption"] !== void 0) {
57332
+ const jb = input["JobBookmarksEncryption"];
57333
+ const item = {};
57334
+ if (typeof jb["JobBookmarksEncryptionMode"] === "string") {
57335
+ item.JobBookmarksEncryptionMode = jb["JobBookmarksEncryptionMode"];
57336
+ }
57337
+ if (typeof jb["KmsKeyArn"] === "string") {
57338
+ item.KmsKeyArn = jb["KmsKeyArn"];
57339
+ }
57340
+ result.JobBookmarksEncryption = item;
57341
+ }
57342
+ return result;
57343
+ }
57344
+ function mapEncryptionConfigurationToCfn(cfg) {
57345
+ const c = cfg ?? {};
57346
+ return {
57347
+ S3Encryption: (c.S3Encryption ?? []).map((entry) => {
57348
+ const out = {};
57349
+ if (entry.S3EncryptionMode !== void 0)
57350
+ out["S3EncryptionMode"] = entry.S3EncryptionMode;
57351
+ if (entry.KmsKeyArn !== void 0)
57352
+ out["KmsKeyArn"] = entry.KmsKeyArn;
57353
+ return out;
57354
+ }),
57355
+ CloudWatchEncryption: c.CloudWatchEncryption ? cleanCfnObject({
57356
+ CloudWatchEncryptionMode: c.CloudWatchEncryption.CloudWatchEncryptionMode,
57357
+ KmsKeyArn: c.CloudWatchEncryption.KmsKeyArn
57358
+ }) : {},
57359
+ JobBookmarksEncryption: c.JobBookmarksEncryption ? cleanCfnObject({
57360
+ JobBookmarksEncryptionMode: c.JobBookmarksEncryption.JobBookmarksEncryptionMode,
57361
+ KmsKeyArn: c.JobBookmarksEncryption.KmsKeyArn
57362
+ }) : {}
57363
+ };
57364
+ }
57365
+ function cleanCfnObject(obj) {
57366
+ const out = {};
57367
+ for (const [k, v] of Object.entries(obj)) {
57368
+ if (v !== void 0)
57369
+ out[k] = v;
57370
+ }
57371
+ return out;
57372
+ }
57373
+ async function buildGlueResourceArn(client, stsClient, resource, name, accountId) {
57374
+ const region = await client.config.region() || process.env["AWS_REGION"] || "us-east-1";
57375
+ const account = accountId ?? await resolveAccountId(stsClient);
57376
+ return `arn:aws:glue:${region}:${account}:${resource}/${name}`;
57377
+ }
57378
+ async function resolveAccountId(stsClient) {
57379
+ const identity = await stsClient.send(new GetCallerIdentityCommand8({}));
57380
+ if (!identity.Account) {
57381
+ throw new Error("Failed to resolve AWS account id from STS");
57382
+ }
57383
+ return identity.Account;
57384
+ }
57385
+ async function fetchGlueTags(client, stsClient, resource, name, accountId, logger) {
57386
+ try {
57387
+ const arn = await buildGlueResourceArn(client, stsClient, resource, name, accountId);
57388
+ const resp = await client.send(new GetTagsCommand({ ResourceArn: arn }));
57389
+ return normalizeAwsTagsToCfn(resp.Tags);
57390
+ } catch (err) {
57391
+ logger.debug(
57392
+ `GetTags failed for ${resource}/${name}: ${err instanceof Error ? err.message : String(err)}`
57393
+ );
57394
+ return [];
57395
+ }
57396
+ }
57397
+ var GlueJobProvider = class {
57398
+ client;
57399
+ stsClient;
57400
+ cachedAccountId;
57401
+ providerRegion = process.env["AWS_REGION"];
57402
+ logger = getLogger().child("GlueJobProvider");
57403
+ handledProperties = /* @__PURE__ */ new Map([
57404
+ [
57405
+ "AWS::Glue::Job",
57406
+ /* @__PURE__ */ new Set([
57407
+ "Name",
57408
+ "Role",
57409
+ "Command",
57410
+ "Description",
57411
+ "MaxCapacity",
57412
+ "MaxRetries",
57413
+ "Timeout",
57414
+ "ExecutionProperty",
57415
+ "GlueVersion",
57416
+ "NumberOfWorkers",
57417
+ "WorkerType",
57418
+ "DefaultArguments",
57419
+ "NonOverridableArguments",
57420
+ "Connections",
57421
+ "LogUri",
57422
+ "SecurityConfiguration",
57423
+ "NotificationProperty",
57424
+ "ExecutionClass",
57425
+ "JobMode",
57426
+ "JobRunQueuingEnabled",
57427
+ "MaintenanceWindow",
57428
+ "AllocatedCapacity",
57429
+ "SourceControlDetails",
57430
+ "Tags"
57431
+ ])
57432
+ ]
57433
+ ]);
57434
+ getClient() {
57435
+ if (!this.client) {
57436
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
57437
+ }
57438
+ return this.client;
57439
+ }
57440
+ getStsClient() {
57441
+ if (!this.stsClient) {
57442
+ this.stsClient = new STSClient8(this.providerRegion ? { region: this.providerRegion } : {});
57443
+ }
57444
+ return this.stsClient;
57445
+ }
57446
+ async create(logicalId, resourceType, properties) {
57447
+ this.logger.debug(`Creating Glue Job ${logicalId}`);
57448
+ const name = properties["Name"] ?? logicalId;
57449
+ const role = properties["Role"];
57450
+ const command = properties["Command"];
57451
+ if (!role) {
57452
+ throw new ProvisioningError(
57453
+ `Role is required for Glue Job ${logicalId}`,
57454
+ resourceType,
57455
+ logicalId
57456
+ );
57457
+ }
57458
+ if (!command) {
57459
+ throw new ProvisioningError(
57460
+ `Command is required for Glue Job ${logicalId}`,
57461
+ resourceType,
57462
+ logicalId
57463
+ );
57464
+ }
57465
+ try {
57466
+ const tags = cfnTagsToMap(properties["Tags"]);
57467
+ await this.getClient().send(
57468
+ new CreateJobCommand({
57469
+ Name: name,
57470
+ Role: role,
57471
+ Command: buildJobCommand(command),
57472
+ ...buildJobCommonFields(properties),
57473
+ ...tags && { Tags: tags }
57474
+ })
57475
+ );
57476
+ this.logger.debug(`Successfully created Glue Job ${logicalId}: ${name}`);
57477
+ return { physicalId: name, attributes: {} };
57478
+ } catch (error) {
57479
+ const cause = error instanceof Error ? error : void 0;
57480
+ throw new ProvisioningError(
57481
+ `Failed to create Glue Job ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57482
+ resourceType,
57483
+ logicalId,
57484
+ void 0,
57485
+ cause
57486
+ );
57487
+ }
57488
+ }
57489
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
57490
+ this.logger.debug(`Updating Glue Job ${logicalId}: ${physicalId}`);
57491
+ try {
57492
+ const command = properties["Command"];
57493
+ const update = {
57494
+ ...command !== void 0 && { Command: buildJobCommand(command) },
57495
+ ...buildJobCommonFields(properties),
57496
+ // Role is required at create but mutable on update; include only when defined
57497
+ ...properties["Role"] !== void 0 && { Role: properties["Role"] }
57498
+ };
57499
+ await this.getClient().send(new UpdateJobCommand({ JobName: physicalId, JobUpdate: update }));
57500
+ const oldTags = cfnTagsToMap(previousProperties["Tags"]) ?? {};
57501
+ const newTags = cfnTagsToMap(properties["Tags"]) ?? {};
57502
+ await this.applyTagDiff(physicalId, oldTags, newTags);
57503
+ return { physicalId, wasReplaced: false };
57504
+ } catch (error) {
57505
+ const cause = error instanceof Error ? error : void 0;
57506
+ throw new ProvisioningError(
57507
+ `Failed to update Glue Job ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57508
+ resourceType,
57509
+ logicalId,
57510
+ physicalId,
57511
+ cause
57512
+ );
57513
+ }
57514
+ }
57515
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
57516
+ this.logger.debug(`Deleting Glue Job ${logicalId}: ${physicalId}`);
57517
+ try {
57518
+ await this.getClient().send(new DeleteJobCommand({ JobName: physicalId }));
57519
+ } catch (error) {
57520
+ if (error instanceof EntityNotFoundException) {
57521
+ const clientRegion = await this.getClient().config.region();
57522
+ assertRegionMatch(
57523
+ clientRegion,
57524
+ context?.expectedRegion,
57525
+ resourceType,
57526
+ logicalId,
57527
+ physicalId
57528
+ );
57529
+ this.logger.debug(`Glue Job ${physicalId} does not exist, skipping deletion`);
57530
+ return;
57531
+ }
57532
+ const cause = error instanceof Error ? error : void 0;
57533
+ throw new ProvisioningError(
57534
+ `Failed to delete Glue Job ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57535
+ resourceType,
57536
+ logicalId,
57537
+ physicalId,
57538
+ cause
57539
+ );
57540
+ }
57541
+ }
57542
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
57543
+ async getAttribute(physicalId, _resourceType, attributeName) {
57544
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
57545
+ return physicalId;
57546
+ }
57547
+ return void 0;
57548
+ }
57549
+ /**
57550
+ * Read the AWS-current Glue Job in CFn-property shape.
57551
+ *
57552
+ * Always-emit placeholders for user-controllable top-level keys per
57553
+ * PR #145 (`?? '' | [] | {}`) so the v3 `observedProperties` baseline
57554
+ * detects console-side ADDs to fields that weren't templated. Tags
57555
+ * always emit `[]` (PR H pattern).
57556
+ */
57557
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
57558
+ let job;
57559
+ try {
57560
+ const resp = await this.getClient().send(new GetJobCommand({ JobName: physicalId }));
57561
+ job = resp.Job;
57562
+ } catch (err) {
57563
+ if (err instanceof EntityNotFoundException)
57564
+ return void 0;
57565
+ throw err;
57566
+ }
57567
+ if (!job)
57568
+ return void 0;
57569
+ const result = {
57570
+ Name: job.Name ?? physicalId,
57571
+ Role: job.Role ?? "",
57572
+ Command: pickDefined({
57573
+ Name: job.Command?.Name,
57574
+ ScriptLocation: job.Command?.ScriptLocation,
57575
+ PythonVersion: job.Command?.PythonVersion,
57576
+ Runtime: job.Command?.Runtime
57577
+ }),
57578
+ Description: job.Description ?? "",
57579
+ LogUri: job.LogUri ?? "",
57580
+ DefaultArguments: job.DefaultArguments ?? {},
57581
+ NonOverridableArguments: job.NonOverridableArguments ?? {},
57582
+ Connections: { Connections: job.Connections?.Connections ?? [] },
57583
+ MaxRetries: job.MaxRetries ?? 0,
57584
+ Timeout: job.Timeout ?? 0,
57585
+ ExecutionProperty: { MaxConcurrentRuns: job.ExecutionProperty?.MaxConcurrentRuns ?? 1 },
57586
+ NotificationProperty: { NotifyDelayAfter: job.NotificationProperty?.NotifyDelayAfter ?? 0 },
57587
+ GlueVersion: job.GlueVersion ?? "",
57588
+ NumberOfWorkers: job.NumberOfWorkers ?? 0,
57589
+ WorkerType: job.WorkerType ?? "",
57590
+ MaxCapacity: job.MaxCapacity ?? 0,
57591
+ AllocatedCapacity: job.AllocatedCapacity ?? 0,
57592
+ SecurityConfiguration: job.SecurityConfiguration ?? "",
57593
+ ExecutionClass: job.ExecutionClass ?? "",
57594
+ JobMode: job.JobMode ?? "",
57595
+ JobRunQueuingEnabled: job.JobRunQueuingEnabled ?? false,
57596
+ MaintenanceWindow: job.MaintenanceWindow ?? "",
57597
+ SourceControlDetails: job.SourceControlDetails ? pickDefined(job.SourceControlDetails) : {}
57598
+ };
57599
+ result["Tags"] = await fetchGlueTags(
57600
+ this.getClient(),
57601
+ this.getStsClient(),
57602
+ "job",
57603
+ job.Name ?? physicalId,
57604
+ this.cachedAccountId,
57605
+ this.logger
57606
+ );
57607
+ return result;
57608
+ }
57609
+ async applyTagDiff(physicalId, oldTags, newTags) {
57610
+ const arn = await buildGlueResourceArn(
57611
+ this.getClient(),
57612
+ this.getStsClient(),
57613
+ "job",
57614
+ physicalId,
57615
+ this.cachedAccountId
57616
+ );
57617
+ const toAdd = {};
57618
+ const toRemove = [];
57619
+ for (const [k, v] of Object.entries(newTags)) {
57620
+ if (oldTags[k] !== v)
57621
+ toAdd[k] = v;
57622
+ }
57623
+ for (const k of Object.keys(oldTags)) {
57624
+ if (!(k in newTags))
57625
+ toRemove.push(k);
57626
+ }
57627
+ if (Object.keys(toAdd).length > 0 || toRemove.length > 0) {
57628
+ const { TagResourceCommand: TagResourceCommand17, UntagResourceCommand: UntagResourceCommand16 } = await import("@aws-sdk/client-glue");
57629
+ if (Object.keys(toAdd).length > 0) {
57630
+ await this.getClient().send(new TagResourceCommand17({ ResourceArn: arn, TagsToAdd: toAdd }));
57631
+ }
57632
+ if (toRemove.length > 0) {
57633
+ await this.getClient().send(
57634
+ new UntagResourceCommand16({ ResourceArn: arn, TagsToRemove: toRemove })
57635
+ );
57636
+ }
57637
+ }
57638
+ }
57639
+ async import(input) {
57640
+ const explicitName = input.knownPhysicalId ?? input.properties["Name"];
57641
+ if (!explicitName)
57642
+ return null;
57643
+ try {
57644
+ await this.getClient().send(new GetJobCommand({ JobName: explicitName }));
57645
+ return { physicalId: explicitName, attributes: {} };
57646
+ } catch (err) {
57647
+ if (err instanceof EntityNotFoundException)
57648
+ return null;
57649
+ throw err;
57650
+ }
57651
+ }
57652
+ };
57653
+ function buildJobCommand(c) {
57654
+ const result = {};
57655
+ if (c["Name"] !== void 0)
57656
+ result.Name = c["Name"];
57657
+ if (c["ScriptLocation"] !== void 0)
57658
+ result.ScriptLocation = c["ScriptLocation"];
57659
+ if (c["PythonVersion"] !== void 0)
57660
+ result.PythonVersion = c["PythonVersion"];
57661
+ if (c["Runtime"] !== void 0)
57662
+ result.Runtime = c["Runtime"];
57663
+ return result;
57664
+ }
57665
+ function buildJobCommonFields(p) {
57666
+ const r = {};
57667
+ const passThrough = [
57668
+ "JobMode",
57669
+ "JobRunQueuingEnabled",
57670
+ "Description",
57671
+ "LogUri",
57672
+ "DefaultArguments",
57673
+ "NonOverridableArguments",
57674
+ "MaxRetries",
57675
+ "AllocatedCapacity",
57676
+ "Timeout",
57677
+ "MaxCapacity",
57678
+ "WorkerType",
57679
+ "NumberOfWorkers",
57680
+ "SecurityConfiguration",
57681
+ "GlueVersion",
57682
+ "ExecutionClass",
57683
+ "MaintenanceWindow"
57684
+ ];
57685
+ for (const k of passThrough) {
57686
+ if (p[k] !== void 0) {
57687
+ r[k] = p[k];
57688
+ }
57689
+ }
57690
+ if (p["ExecutionProperty"] !== void 0) {
57691
+ r.ExecutionProperty = p["ExecutionProperty"];
57692
+ }
57693
+ if (p["Connections"] !== void 0) {
57694
+ const conn = p["Connections"];
57695
+ r.Connections = { Connections: conn["Connections"] ?? [] };
57696
+ }
57697
+ if (p["NotificationProperty"] !== void 0) {
57698
+ r.NotificationProperty = p["NotificationProperty"];
57699
+ }
57700
+ if (p["SourceControlDetails"] !== void 0) {
57701
+ r.SourceControlDetails = p["SourceControlDetails"];
57702
+ }
57703
+ return r;
57704
+ }
57705
+ function cfnTagsToMap(tagsInput) {
57706
+ if (tagsInput === void 0)
57707
+ return void 0;
57708
+ const out = {};
57709
+ if (Array.isArray(tagsInput)) {
57710
+ for (const entry of tagsInput) {
57711
+ const e = entry;
57712
+ const k = e["Key"];
57713
+ const v = e["Value"];
57714
+ if (typeof k === "string")
57715
+ out[k] = typeof v === "string" ? v : "";
57716
+ }
57717
+ return out;
57718
+ }
57719
+ if (typeof tagsInput === "object" && tagsInput !== null) {
57720
+ for (const [k, v] of Object.entries(tagsInput)) {
57721
+ out[k] = typeof v === "string" ? v : "";
57722
+ }
57723
+ return out;
57724
+ }
57725
+ return out;
57726
+ }
57727
+ function pickDefined(obj) {
57728
+ const out = {};
57729
+ for (const [k, v] of Object.entries(obj)) {
57730
+ if (v === void 0 || v === null)
57731
+ continue;
57732
+ if (typeof v === "object" && !Array.isArray(v)) {
57733
+ const inner = pickDefined(v);
57734
+ out[k] = inner;
57735
+ } else {
57736
+ out[k] = v;
57737
+ }
57738
+ }
57739
+ return out;
57740
+ }
57741
+ var GlueCrawlerProvider = class {
57742
+ client;
57743
+ stsClient;
57744
+ cachedAccountId;
57745
+ providerRegion = process.env["AWS_REGION"];
57746
+ logger = getLogger().child("GlueCrawlerProvider");
57747
+ handledProperties = /* @__PURE__ */ new Map([
57748
+ [
57749
+ "AWS::Glue::Crawler",
57750
+ /* @__PURE__ */ new Set([
57751
+ "Name",
57752
+ "Role",
57753
+ "Targets",
57754
+ "DatabaseName",
57755
+ "Description",
57756
+ "Schedule",
57757
+ "Classifiers",
57758
+ "TablePrefix",
57759
+ "SchemaChangePolicy",
57760
+ "RecrawlPolicy",
57761
+ "LineageConfiguration",
57762
+ "LakeFormationConfiguration",
57763
+ "Configuration",
57764
+ "CrawlerSecurityConfiguration",
57765
+ "Tags"
57766
+ ])
57767
+ ]
57768
+ ]);
57769
+ getClient() {
57770
+ if (!this.client) {
57771
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
57772
+ }
57773
+ return this.client;
57774
+ }
57775
+ getStsClient() {
57776
+ if (!this.stsClient) {
57777
+ this.stsClient = new STSClient8(this.providerRegion ? { region: this.providerRegion } : {});
57778
+ }
57779
+ return this.stsClient;
57780
+ }
57781
+ async create(logicalId, resourceType, properties) {
57782
+ this.logger.debug(`Creating Glue Crawler ${logicalId}`);
57783
+ const name = properties["Name"] ?? logicalId;
57784
+ const role = properties["Role"];
57785
+ const targets = properties["Targets"];
57786
+ if (!role) {
57787
+ throw new ProvisioningError(
57788
+ `Role is required for Glue Crawler ${logicalId}`,
57789
+ resourceType,
57790
+ logicalId
57791
+ );
57792
+ }
57793
+ if (!targets) {
57794
+ throw new ProvisioningError(
57795
+ `Targets is required for Glue Crawler ${logicalId}`,
57796
+ resourceType,
57797
+ logicalId
57798
+ );
57799
+ }
57800
+ try {
57801
+ const tags = cfnTagsToMap(properties["Tags"]);
57802
+ await this.getClient().send(
57803
+ new CreateCrawlerCommand({
57804
+ Name: name,
57805
+ Role: role,
57806
+ Targets: targets,
57807
+ ...buildCrawlerCommonFields(properties),
57808
+ ...tags && { Tags: tags }
57809
+ })
57810
+ );
57811
+ this.logger.debug(`Successfully created Glue Crawler ${logicalId}: ${name}`);
57812
+ return { physicalId: name, attributes: {} };
57813
+ } catch (error) {
57814
+ const cause = error instanceof Error ? error : void 0;
57815
+ throw new ProvisioningError(
57816
+ `Failed to create Glue Crawler ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57817
+ resourceType,
57818
+ logicalId,
57819
+ void 0,
57820
+ cause
57821
+ );
57822
+ }
57823
+ }
57824
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
57825
+ this.logger.debug(`Updating Glue Crawler ${logicalId}: ${physicalId}`);
57826
+ try {
57827
+ await this.getClient().send(
57828
+ new UpdateCrawlerCommand({
57829
+ Name: physicalId,
57830
+ ...properties["Role"] !== void 0 && { Role: properties["Role"] },
57831
+ ...properties["Targets"] !== void 0 && {
57832
+ Targets: properties["Targets"]
57833
+ },
57834
+ ...buildCrawlerCommonFields(properties)
57835
+ })
57836
+ );
57837
+ const oldTags = cfnTagsToMap(previousProperties["Tags"]) ?? {};
57838
+ const newTags = cfnTagsToMap(properties["Tags"]) ?? {};
57839
+ await this.applyTagDiff(physicalId, oldTags, newTags);
57840
+ return { physicalId, wasReplaced: false };
57841
+ } catch (error) {
57842
+ const cause = error instanceof Error ? error : void 0;
57843
+ throw new ProvisioningError(
57844
+ `Failed to update Glue Crawler ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57845
+ resourceType,
57846
+ logicalId,
57847
+ physicalId,
57848
+ cause
57849
+ );
57850
+ }
57851
+ }
57852
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
57853
+ this.logger.debug(`Deleting Glue Crawler ${logicalId}: ${physicalId}`);
57854
+ try {
57855
+ await this.getClient().send(new DeleteCrawlerCommand({ Name: physicalId }));
57856
+ } catch (error) {
57857
+ if (error instanceof EntityNotFoundException) {
57858
+ const clientRegion = await this.getClient().config.region();
57859
+ assertRegionMatch(
57860
+ clientRegion,
57861
+ context?.expectedRegion,
57862
+ resourceType,
57863
+ logicalId,
57864
+ physicalId
57865
+ );
57866
+ this.logger.debug(`Glue Crawler ${physicalId} does not exist, skipping deletion`);
57867
+ return;
57868
+ }
57869
+ const cause = error instanceof Error ? error : void 0;
57870
+ throw new ProvisioningError(
57871
+ `Failed to delete Glue Crawler ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
57872
+ resourceType,
57873
+ logicalId,
57874
+ physicalId,
57875
+ cause
57876
+ );
57877
+ }
57878
+ }
57879
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
57880
+ async getAttribute(physicalId, _resourceType, attributeName) {
57881
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
57882
+ return physicalId;
57883
+ }
57884
+ return void 0;
57885
+ }
57886
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
57887
+ let crawler;
57888
+ try {
57889
+ const resp = await this.getClient().send(new GetCrawlerCommand({ Name: physicalId }));
57890
+ crawler = resp.Crawler;
57891
+ } catch (err) {
57892
+ if (err instanceof EntityNotFoundException)
57893
+ return void 0;
57894
+ throw err;
57895
+ }
57896
+ if (!crawler)
57897
+ return void 0;
57898
+ const result = {
57899
+ Name: crawler.Name ?? physicalId,
57900
+ Role: crawler.Role ?? "",
57901
+ Targets: crawler.Targets ? pickDefined(crawler.Targets) : {},
57902
+ DatabaseName: crawler.DatabaseName ?? "",
57903
+ Description: crawler.Description ?? "",
57904
+ // CFn `Schedule` is the structured wrapper; reverse-map from the
57905
+ // SDK's `Schedule { ScheduleExpression, State }` Description shape.
57906
+ Schedule: crawler.Schedule?.ScheduleExpression ? { ScheduleExpression: crawler.Schedule.ScheduleExpression } : {},
57907
+ Classifiers: crawler.Classifiers ?? [],
57908
+ TablePrefix: crawler.TablePrefix ?? "",
57909
+ SchemaChangePolicy: crawler.SchemaChangePolicy ? pickDefined(crawler.SchemaChangePolicy) : {},
57910
+ RecrawlPolicy: crawler.RecrawlPolicy ? pickDefined(crawler.RecrawlPolicy) : {},
57911
+ LineageConfiguration: crawler.LineageConfiguration ? pickDefined(crawler.LineageConfiguration) : {},
57912
+ LakeFormationConfiguration: crawler.LakeFormationConfiguration ? pickDefined(crawler.LakeFormationConfiguration) : {},
57913
+ Configuration: crawler.Configuration ?? "",
57914
+ CrawlerSecurityConfiguration: crawler.CrawlerSecurityConfiguration ?? ""
57915
+ };
57916
+ result["Tags"] = await fetchGlueTags(
57917
+ this.getClient(),
57918
+ this.getStsClient(),
57919
+ "crawler",
57920
+ crawler.Name ?? physicalId,
57921
+ this.cachedAccountId,
57922
+ this.logger
57923
+ );
57924
+ return result;
57925
+ }
57926
+ /**
57927
+ * Start (or stop) a crawler's schedule. Exposed for downstream tooling
57928
+ * — not part of `update()` because AWS treats schedule activation as a
57929
+ * separate side-effect from crawler config update.
57930
+ */
57931
+ async startSchedule(physicalId) {
57932
+ await this.getClient().send(new StartCrawlerScheduleCommand({ CrawlerName: physicalId }));
57933
+ }
57934
+ async stopSchedule(physicalId) {
57935
+ await this.getClient().send(new StopCrawlerScheduleCommand({ CrawlerName: physicalId }));
57936
+ }
57937
+ async applyTagDiff(physicalId, oldTags, newTags) {
57938
+ const arn = await buildGlueResourceArn(
57939
+ this.getClient(),
57940
+ this.getStsClient(),
57941
+ "crawler",
57942
+ physicalId,
57943
+ this.cachedAccountId
57944
+ );
57945
+ const toAdd = {};
57946
+ const toRemove = [];
57947
+ for (const [k, v] of Object.entries(newTags)) {
57948
+ if (oldTags[k] !== v)
57949
+ toAdd[k] = v;
57950
+ }
57951
+ for (const k of Object.keys(oldTags)) {
57952
+ if (!(k in newTags))
57953
+ toRemove.push(k);
57954
+ }
57955
+ if (Object.keys(toAdd).length > 0 || toRemove.length > 0) {
57956
+ const { TagResourceCommand: TagResourceCommand17, UntagResourceCommand: UntagResourceCommand16 } = await import("@aws-sdk/client-glue");
57957
+ if (Object.keys(toAdd).length > 0) {
57958
+ await this.getClient().send(new TagResourceCommand17({ ResourceArn: arn, TagsToAdd: toAdd }));
57959
+ }
57960
+ if (toRemove.length > 0) {
57961
+ await this.getClient().send(
57962
+ new UntagResourceCommand16({ ResourceArn: arn, TagsToRemove: toRemove })
57963
+ );
57964
+ }
57965
+ }
57966
+ }
57967
+ async import(input) {
57968
+ const explicitName = input.knownPhysicalId ?? input.properties["Name"];
57969
+ if (!explicitName)
57970
+ return null;
57971
+ try {
57972
+ await this.getClient().send(new GetCrawlerCommand({ Name: explicitName }));
57973
+ return { physicalId: explicitName, attributes: {} };
57974
+ } catch (err) {
57975
+ if (err instanceof EntityNotFoundException)
57976
+ return null;
57977
+ throw err;
57978
+ }
57979
+ }
57980
+ };
57981
+ function buildCrawlerCommonFields(p) {
57982
+ const r = {};
57983
+ if (p["DatabaseName"] !== void 0)
57984
+ r["DatabaseName"] = p["DatabaseName"];
57985
+ if (p["Description"] !== void 0)
57986
+ r["Description"] = p["Description"];
57987
+ if (p["Classifiers"] !== void 0)
57988
+ r["Classifiers"] = p["Classifiers"];
57989
+ if (p["TablePrefix"] !== void 0)
57990
+ r["TablePrefix"] = p["TablePrefix"];
57991
+ if (p["Schedule"] !== void 0) {
57992
+ const sched = p["Schedule"];
57993
+ if (typeof sched === "string") {
57994
+ r["Schedule"] = sched;
57995
+ } else if (typeof sched === "object" && sched !== null) {
57996
+ const wrap = sched;
57997
+ if (wrap["ScheduleExpression"] !== void 0) {
57998
+ r["Schedule"] = wrap["ScheduleExpression"];
57999
+ }
58000
+ }
58001
+ }
58002
+ if (p["SchemaChangePolicy"] !== void 0) {
58003
+ r["SchemaChangePolicy"] = p["SchemaChangePolicy"];
58004
+ }
58005
+ if (p["RecrawlPolicy"] !== void 0) {
58006
+ r["RecrawlPolicy"] = p["RecrawlPolicy"];
58007
+ }
58008
+ if (p["LineageConfiguration"] !== void 0) {
58009
+ r["LineageConfiguration"] = p["LineageConfiguration"];
58010
+ }
58011
+ if (p["LakeFormationConfiguration"] !== void 0) {
58012
+ r["LakeFormationConfiguration"] = p["LakeFormationConfiguration"];
58013
+ }
58014
+ if (p["Configuration"] !== void 0)
58015
+ r["Configuration"] = p["Configuration"];
58016
+ if (p["CrawlerSecurityConfiguration"] !== void 0) {
58017
+ r["CrawlerSecurityConfiguration"] = p["CrawlerSecurityConfiguration"];
58018
+ }
58019
+ return r;
58020
+ }
58021
+ var GlueConnectionProvider = class {
58022
+ client;
58023
+ providerRegion = process.env["AWS_REGION"];
58024
+ logger = getLogger().child("GlueConnectionProvider");
58025
+ handledProperties = /* @__PURE__ */ new Map([
58026
+ ["AWS::Glue::Connection", /* @__PURE__ */ new Set(["ConnectionInput", "CatalogId"])]
58027
+ ]);
58028
+ getClient() {
58029
+ if (!this.client) {
58030
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
58031
+ }
58032
+ return this.client;
58033
+ }
58034
+ async create(logicalId, resourceType, properties) {
58035
+ this.logger.debug(`Creating Glue Connection ${logicalId}`);
58036
+ const connectionInput = properties["ConnectionInput"];
58037
+ if (!connectionInput) {
58038
+ throw new ProvisioningError(
58039
+ `ConnectionInput is required for Glue Connection ${logicalId}`,
58040
+ resourceType,
58041
+ logicalId
58042
+ );
58043
+ }
58044
+ const name = connectionInput["Name"] ?? logicalId;
58045
+ const catalogId = properties["CatalogId"];
58046
+ try {
58047
+ await this.getClient().send(
58048
+ new CreateConnectionCommand({
58049
+ ...catalogId && { CatalogId: catalogId },
58050
+ ConnectionInput: buildConnectionInput(connectionInput, name)
58051
+ })
58052
+ );
58053
+ this.logger.debug(`Successfully created Glue Connection ${logicalId}: ${name}`);
58054
+ return { physicalId: name, attributes: {} };
58055
+ } catch (error) {
58056
+ const cause = error instanceof Error ? error : void 0;
58057
+ throw new ProvisioningError(
58058
+ `Failed to create Glue Connection ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58059
+ resourceType,
58060
+ logicalId,
58061
+ void 0,
58062
+ cause
58063
+ );
58064
+ }
58065
+ }
58066
+ async update(logicalId, physicalId, resourceType, properties, _previousProperties) {
58067
+ this.logger.debug(`Updating Glue Connection ${logicalId}: ${physicalId}`);
58068
+ const connectionInput = properties["ConnectionInput"];
58069
+ if (!connectionInput) {
58070
+ throw new ProvisioningError(
58071
+ `ConnectionInput is required for Glue Connection update ${logicalId}`,
58072
+ resourceType,
58073
+ logicalId,
58074
+ physicalId
58075
+ );
58076
+ }
58077
+ const catalogId = properties["CatalogId"];
58078
+ try {
58079
+ await this.getClient().send(
58080
+ new UpdateConnectionCommand({
58081
+ ...catalogId && { CatalogId: catalogId },
58082
+ Name: physicalId,
58083
+ ConnectionInput: buildConnectionInput(connectionInput, physicalId)
58084
+ })
58085
+ );
58086
+ return { physicalId, wasReplaced: false };
58087
+ } catch (error) {
58088
+ const cause = error instanceof Error ? error : void 0;
58089
+ throw new ProvisioningError(
58090
+ `Failed to update Glue Connection ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58091
+ resourceType,
58092
+ logicalId,
58093
+ physicalId,
58094
+ cause
58095
+ );
58096
+ }
58097
+ }
58098
+ async delete(logicalId, physicalId, resourceType, properties, context) {
58099
+ this.logger.debug(`Deleting Glue Connection ${logicalId}: ${physicalId}`);
58100
+ const catalogId = properties?.["CatalogId"];
58101
+ try {
58102
+ await this.getClient().send(
58103
+ new DeleteConnectionCommand({
58104
+ ConnectionName: physicalId,
58105
+ ...catalogId && { CatalogId: catalogId }
58106
+ })
58107
+ );
58108
+ } catch (error) {
58109
+ if (error instanceof EntityNotFoundException) {
58110
+ const clientRegion = await this.getClient().config.region();
58111
+ assertRegionMatch(
58112
+ clientRegion,
58113
+ context?.expectedRegion,
58114
+ resourceType,
58115
+ logicalId,
58116
+ physicalId
58117
+ );
58118
+ this.logger.debug(`Glue Connection ${physicalId} does not exist, skipping deletion`);
58119
+ return;
58120
+ }
58121
+ const cause = error instanceof Error ? error : void 0;
58122
+ throw new ProvisioningError(
58123
+ `Failed to delete Glue Connection ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58124
+ resourceType,
58125
+ logicalId,
58126
+ physicalId,
58127
+ cause
58128
+ );
58129
+ }
58130
+ }
58131
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
58132
+ async getAttribute(physicalId, _resourceType, attributeName) {
58133
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
58134
+ return physicalId;
58135
+ }
58136
+ return void 0;
58137
+ }
58138
+ async readCurrentState(physicalId, _logicalId, _resourceType, properties) {
58139
+ const catalogId = properties?.["CatalogId"];
58140
+ let conn;
58141
+ try {
58142
+ const resp = await this.getClient().send(
58143
+ new GetConnectionCommand({ Name: physicalId, ...catalogId && { CatalogId: catalogId } })
58144
+ );
58145
+ conn = resp.Connection;
58146
+ } catch (err) {
58147
+ if (err instanceof EntityNotFoundException)
58148
+ return void 0;
58149
+ throw err;
58150
+ }
58151
+ if (!conn)
58152
+ return void 0;
58153
+ const ci = {
58154
+ Name: conn.Name ?? physicalId,
58155
+ ConnectionType: conn.ConnectionType ?? "",
58156
+ Description: conn.Description ?? "",
58157
+ MatchCriteria: conn.MatchCriteria ?? [],
58158
+ ConnectionProperties: conn.ConnectionProperties ?? {},
58159
+ SparkProperties: conn.SparkProperties ?? {},
58160
+ AthenaProperties: conn.AthenaProperties ?? {},
58161
+ PythonProperties: conn.PythonProperties ?? {},
58162
+ PhysicalConnectionRequirements: conn.PhysicalConnectionRequirements ? pickDefined(conn.PhysicalConnectionRequirements) : {}
58163
+ };
58164
+ return { ConnectionInput: ci };
58165
+ }
58166
+ async import(input) {
58167
+ const explicitName = input.knownPhysicalId ?? input.properties["ConnectionInput"]?.["Name"];
58168
+ if (!explicitName)
58169
+ return null;
58170
+ const catalogId = input.properties["CatalogId"];
58171
+ try {
58172
+ await this.getClient().send(
58173
+ new GetConnectionCommand({
58174
+ Name: explicitName,
58175
+ ...catalogId && { CatalogId: catalogId }
58176
+ })
58177
+ );
58178
+ return { physicalId: explicitName, attributes: {} };
58179
+ } catch (err) {
58180
+ if (err instanceof EntityNotFoundException)
58181
+ return null;
58182
+ throw err;
58183
+ }
58184
+ }
58185
+ };
58186
+ function buildConnectionInput(ci, fallbackName) {
58187
+ const result = {
58188
+ Name: ci["Name"] ?? fallbackName,
58189
+ ConnectionType: ci["ConnectionType"],
58190
+ ConnectionProperties: ci["ConnectionProperties"] ?? {}
58191
+ };
58192
+ if (ci["Description"] !== void 0)
58193
+ result.Description = ci["Description"];
58194
+ if (ci["MatchCriteria"] !== void 0)
58195
+ result.MatchCriteria = ci["MatchCriteria"];
58196
+ if (ci["SparkProperties"] !== void 0) {
58197
+ result.SparkProperties = ci["SparkProperties"];
58198
+ }
58199
+ if (ci["AthenaProperties"] !== void 0) {
58200
+ result.AthenaProperties = ci["AthenaProperties"];
58201
+ }
58202
+ if (ci["PythonProperties"] !== void 0) {
58203
+ result.PythonProperties = ci["PythonProperties"];
58204
+ }
58205
+ if (ci["PhysicalConnectionRequirements"] !== void 0) {
58206
+ result.PhysicalConnectionRequirements = ci["PhysicalConnectionRequirements"];
58207
+ }
58208
+ if (ci["AuthenticationConfiguration"] !== void 0) {
58209
+ result.AuthenticationConfiguration = ci["AuthenticationConfiguration"];
58210
+ }
58211
+ if (ci["ValidateCredentials"] !== void 0) {
58212
+ result.ValidateCredentials = ci["ValidateCredentials"];
58213
+ }
58214
+ if (ci["ValidateForComputeEnvironments"] !== void 0) {
58215
+ result.ValidateForComputeEnvironments = ci["ValidateForComputeEnvironments"];
58216
+ }
58217
+ return result;
58218
+ }
58219
+ var GlueTriggerProvider = class {
58220
+ client;
58221
+ stsClient;
58222
+ cachedAccountId;
58223
+ providerRegion = process.env["AWS_REGION"];
58224
+ logger = getLogger().child("GlueTriggerProvider");
58225
+ handledProperties = /* @__PURE__ */ new Map([
58226
+ [
58227
+ "AWS::Glue::Trigger",
58228
+ /* @__PURE__ */ new Set([
58229
+ "Name",
58230
+ "Type",
58231
+ "Schedule",
58232
+ "Actions",
58233
+ "Predicate",
58234
+ "Description",
58235
+ "StartOnCreation",
58236
+ "EventBatchingCondition",
58237
+ "WorkflowName",
58238
+ "Tags"
58239
+ ])
58240
+ ]
58241
+ ]);
58242
+ getClient() {
58243
+ if (!this.client) {
58244
+ this.client = new GlueClient(this.providerRegion ? { region: this.providerRegion } : {});
58245
+ }
58246
+ return this.client;
58247
+ }
58248
+ getStsClient() {
58249
+ if (!this.stsClient) {
58250
+ this.stsClient = new STSClient8(this.providerRegion ? { region: this.providerRegion } : {});
58251
+ }
58252
+ return this.stsClient;
58253
+ }
58254
+ async create(logicalId, resourceType, properties) {
58255
+ this.logger.debug(`Creating Glue Trigger ${logicalId}`);
58256
+ const name = properties["Name"] ?? logicalId;
58257
+ const type = properties["Type"];
58258
+ const actions = properties["Actions"];
58259
+ if (!type) {
58260
+ throw new ProvisioningError(
58261
+ `Type is required for Glue Trigger ${logicalId}`,
58262
+ resourceType,
58263
+ logicalId
58264
+ );
58265
+ }
58266
+ if (!actions) {
58267
+ throw new ProvisioningError(
58268
+ `Actions is required for Glue Trigger ${logicalId}`,
58269
+ resourceType,
58270
+ logicalId
58271
+ );
58272
+ }
58273
+ try {
58274
+ const tags = cfnTagsToMap(properties["Tags"]);
58275
+ await this.getClient().send(
58276
+ new CreateTriggerCommand({
58277
+ Name: name,
58278
+ Type: type,
58279
+ Actions: actions,
58280
+ ...properties["Schedule"] !== void 0 && {
58281
+ Schedule: properties["Schedule"]
58282
+ },
58283
+ ...properties["Predicate"] !== void 0 && {
58284
+ Predicate: properties["Predicate"]
58285
+ },
58286
+ ...properties["Description"] !== void 0 && {
58287
+ Description: properties["Description"]
58288
+ },
58289
+ ...properties["StartOnCreation"] !== void 0 && {
58290
+ StartOnCreation: properties["StartOnCreation"]
58291
+ },
58292
+ ...properties["WorkflowName"] !== void 0 && {
58293
+ WorkflowName: properties["WorkflowName"]
58294
+ },
58295
+ ...properties["EventBatchingCondition"] !== void 0 && {
58296
+ EventBatchingCondition: properties["EventBatchingCondition"]
58297
+ },
58298
+ ...tags && { Tags: tags }
58299
+ })
58300
+ );
58301
+ this.logger.debug(`Successfully created Glue Trigger ${logicalId}: ${name}`);
58302
+ return { physicalId: name, attributes: {} };
58303
+ } catch (error) {
58304
+ const cause = error instanceof Error ? error : void 0;
58305
+ throw new ProvisioningError(
58306
+ `Failed to create Glue Trigger ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58307
+ resourceType,
58308
+ logicalId,
58309
+ void 0,
58310
+ cause
58311
+ );
58312
+ }
58313
+ }
58314
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
58315
+ this.logger.debug(`Updating Glue Trigger ${logicalId}: ${physicalId}`);
58316
+ try {
58317
+ let restart = false;
58318
+ try {
58319
+ const cur = await this.getClient().send(new GetTriggerCommand({ Name: physicalId }));
58320
+ if (cur.Trigger?.State === "ACTIVATED") {
58321
+ restart = true;
58322
+ await this.getClient().send(new StopTriggerCommand({ Name: physicalId }));
58323
+ }
58324
+ } catch (err) {
58325
+ if (!(err instanceof EntityNotFoundException)) {
58326
+ this.logger.debug(
58327
+ `GetTrigger pre-check failed for ${physicalId}; continuing anyway: ${err instanceof Error ? err.message : String(err)}`
58328
+ );
58329
+ }
58330
+ }
58331
+ const update = {
58332
+ ...properties["Description"] !== void 0 && {
58333
+ Description: properties["Description"]
58334
+ },
58335
+ ...properties["Schedule"] !== void 0 && {
58336
+ Schedule: properties["Schedule"]
58337
+ },
58338
+ ...properties["Actions"] !== void 0 && {
58339
+ Actions: properties["Actions"]
58340
+ },
58341
+ ...properties["Predicate"] !== void 0 && {
58342
+ Predicate: properties["Predicate"]
58343
+ },
58344
+ ...properties["EventBatchingCondition"] !== void 0 && {
58345
+ EventBatchingCondition: properties["EventBatchingCondition"]
58346
+ }
58347
+ };
58348
+ await this.getClient().send(
58349
+ new UpdateTriggerCommand({ Name: physicalId, TriggerUpdate: update })
58350
+ );
58351
+ if (restart) {
58352
+ await this.getClient().send(new StartTriggerCommand({ Name: physicalId }));
58353
+ }
58354
+ const oldTags = cfnTagsToMap(previousProperties["Tags"]) ?? {};
58355
+ const newTags = cfnTagsToMap(properties["Tags"]) ?? {};
58356
+ await this.applyTagDiff(physicalId, oldTags, newTags);
58357
+ return { physicalId, wasReplaced: false };
58358
+ } catch (error) {
58359
+ const cause = error instanceof Error ? error : void 0;
58360
+ throw new ProvisioningError(
58361
+ `Failed to update Glue Trigger ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58362
+ resourceType,
58363
+ logicalId,
58364
+ physicalId,
58365
+ cause
58366
+ );
58367
+ }
58368
+ }
58369
+ async delete(logicalId, physicalId, resourceType, _properties, context) {
58370
+ this.logger.debug(`Deleting Glue Trigger ${logicalId}: ${physicalId}`);
58371
+ try {
58372
+ await this.getClient().send(new DeleteTriggerCommand({ Name: physicalId }));
58373
+ } catch (error) {
58374
+ if (error instanceof EntityNotFoundException) {
58375
+ const clientRegion = await this.getClient().config.region();
58376
+ assertRegionMatch(
58377
+ clientRegion,
58378
+ context?.expectedRegion,
58379
+ resourceType,
58380
+ logicalId,
58381
+ physicalId
58382
+ );
58383
+ this.logger.debug(`Glue Trigger ${physicalId} does not exist, skipping deletion`);
58384
+ return;
58385
+ }
58386
+ const cause = error instanceof Error ? error : void 0;
58387
+ throw new ProvisioningError(
58388
+ `Failed to delete Glue Trigger ${logicalId}: ${error instanceof Error ? error.message : String(error)}`,
58389
+ resourceType,
58390
+ logicalId,
58391
+ physicalId,
58392
+ cause
58393
+ );
58394
+ }
58395
+ }
58396
+ // eslint-disable-next-line @typescript-eslint/require-await -- explicit-override-only intentionally has no AWS calls
58397
+ async getAttribute(physicalId, _resourceType, attributeName) {
58398
+ if (attributeName === "Id" || attributeName === "Ref" || attributeName === "Name") {
58399
+ return physicalId;
58400
+ }
58401
+ return void 0;
58402
+ }
58403
+ async readCurrentState(physicalId, _logicalId, _resourceType) {
58404
+ let trig;
58405
+ try {
58406
+ const resp = await this.getClient().send(new GetTriggerCommand({ Name: physicalId }));
58407
+ trig = resp.Trigger;
58408
+ } catch (err) {
58409
+ if (err instanceof EntityNotFoundException)
58410
+ return void 0;
58411
+ throw err;
58412
+ }
58413
+ if (!trig)
58414
+ return void 0;
58415
+ const result = {
58416
+ Name: trig.Name ?? physicalId,
58417
+ Type: trig.Type ?? "",
58418
+ Schedule: trig.Schedule ?? "",
58419
+ Description: trig.Description ?? "",
58420
+ WorkflowName: trig.WorkflowName ?? "",
58421
+ Actions: (trig.Actions ?? []).map(
58422
+ (a) => pickDefined(a)
58423
+ ),
58424
+ Predicate: trig.Predicate ? {
58425
+ Logical: trig.Predicate.Logical ?? "",
58426
+ Conditions: (trig.Predicate.Conditions ?? []).map(
58427
+ (c) => pickDefined(c)
58428
+ )
58429
+ } : {},
58430
+ EventBatchingCondition: trig.EventBatchingCondition ? pickDefined(trig.EventBatchingCondition) : {}
58431
+ };
58432
+ result["Tags"] = await fetchGlueTags(
58433
+ this.getClient(),
58434
+ this.getStsClient(),
58435
+ "trigger",
58436
+ trig.Name ?? physicalId,
58437
+ this.cachedAccountId,
58438
+ this.logger
58439
+ );
58440
+ return result;
58441
+ }
58442
+ async applyTagDiff(physicalId, oldTags, newTags) {
58443
+ const arn = await buildGlueResourceArn(
58444
+ this.getClient(),
58445
+ this.getStsClient(),
58446
+ "trigger",
58447
+ physicalId,
58448
+ this.cachedAccountId
58449
+ );
58450
+ const toAdd = {};
58451
+ const toRemove = [];
58452
+ for (const [k, v] of Object.entries(newTags)) {
58453
+ if (oldTags[k] !== v)
58454
+ toAdd[k] = v;
58455
+ }
58456
+ for (const k of Object.keys(oldTags)) {
58457
+ if (!(k in newTags))
58458
+ toRemove.push(k);
58459
+ }
58460
+ if (Object.keys(toAdd).length > 0 || toRemove.length > 0) {
58461
+ const { TagResourceCommand: TagResourceCommand17, UntagResourceCommand: UntagResourceCommand16 } = await import("@aws-sdk/client-glue");
58462
+ if (Object.keys(toAdd).length > 0) {
58463
+ await this.getClient().send(new TagResourceCommand17({ ResourceArn: arn, TagsToAdd: toAdd }));
58464
+ }
58465
+ if (toRemove.length > 0) {
58466
+ await this.getClient().send(
58467
+ new UntagResourceCommand16({ ResourceArn: arn, TagsToRemove: toRemove })
58468
+ );
58469
+ }
58470
+ }
58471
+ }
58472
+ async import(input) {
58473
+ const explicitName = input.knownPhysicalId ?? input.properties["Name"];
58474
+ if (!explicitName)
58475
+ return null;
58476
+ try {
58477
+ await this.getClient().send(new GetTriggerCommand({ Name: explicitName }));
58478
+ return { physicalId: explicitName, attributes: {} };
58479
+ } catch (err) {
58480
+ if (err instanceof EntityNotFoundException)
58481
+ return null;
58482
+ throw err;
58483
+ }
58484
+ }
58485
+ };
56875
58486
 
56876
58487
  // src/provisioning/providers/kms-provider.ts
56877
58488
  import {
@@ -63553,6 +65164,12 @@ function registerAllProviders(registry) {
63553
65164
  const glueProvider = new GlueProvider();
63554
65165
  registry.register("AWS::Glue::Database", glueProvider);
63555
65166
  registry.register("AWS::Glue::Table", glueProvider);
65167
+ registry.register("AWS::Glue::Workflow", new GlueWorkflowProvider());
65168
+ registry.register("AWS::Glue::SecurityConfiguration", new GlueSecurityConfigurationProvider());
65169
+ registry.register("AWS::Glue::Job", new GlueJobProvider());
65170
+ registry.register("AWS::Glue::Crawler", new GlueCrawlerProvider());
65171
+ registry.register("AWS::Glue::Connection", new GlueConnectionProvider());
65172
+ registry.register("AWS::Glue::Trigger", new GlueTriggerProvider());
63556
65173
  const kmsProvider = new KMSProvider();
63557
65174
  registry.register("AWS::KMS::Key", kmsProvider);
63558
65175
  registry.register("AWS::KMS::Alias", kmsProvider);
@@ -68373,7 +69990,7 @@ function reorderArgs(argv) {
68373
69990
  }
68374
69991
  async function main() {
68375
69992
  const program = new Command14();
68376
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.63.0");
69993
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.65.0");
68377
69994
  program.addCommand(createBootstrapCommand());
68378
69995
  program.addCommand(createSynthCommand());
68379
69996
  program.addCommand(createListCommand());