@peterhauge/apiops-cli 0.1.3-alpha.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/LICENSE.md +21 -0
- package/README.md +135 -0
- package/dist/cli/extract-command.d.ts +12 -0
- package/dist/cli/extract-command.d.ts.map +1 -0
- package/dist/cli/extract-command.js +157 -0
- package/dist/cli/extract-command.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +74 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-command.d.ts +11 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +87 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/publish-command.d.ts +12 -0
- package/dist/cli/publish-command.d.ts.map +1 -0
- package/dist/cli/publish-command.js +159 -0
- package/dist/cli/publish-command.js.map +1 -0
- package/dist/clients/apim-client.d.ts +110 -0
- package/dist/clients/apim-client.d.ts.map +1 -0
- package/dist/clients/apim-client.js +586 -0
- package/dist/clients/apim-client.js.map +1 -0
- package/dist/clients/artifact-store.d.ts +23 -0
- package/dist/clients/artifact-store.d.ts.map +1 -0
- package/dist/clients/artifact-store.js +188 -0
- package/dist/clients/artifact-store.js.map +1 -0
- package/dist/clients/iapim-client.d.ts +52 -0
- package/dist/clients/iapim-client.d.ts.map +1 -0
- package/dist/clients/iapim-client.js +6 -0
- package/dist/clients/iapim-client.js.map +1 -0
- package/dist/clients/iartifact-store.d.ts +50 -0
- package/dist/clients/iartifact-store.d.ts.map +1 -0
- package/dist/clients/iartifact-store.js +6 -0
- package/dist/clients/iartifact-store.js.map +1 -0
- package/dist/lib/auto-generated.d.ts +27 -0
- package/dist/lib/auto-generated.d.ts.map +1 -0
- package/dist/lib/auto-generated.js +34 -0
- package/dist/lib/auto-generated.js.map +1 -0
- package/dist/lib/cloud-config.d.ts +29 -0
- package/dist/lib/cloud-config.d.ts.map +1 -0
- package/dist/lib/cloud-config.js +60 -0
- package/dist/lib/cloud-config.js.map +1 -0
- package/dist/lib/config-loader.d.ts +21 -0
- package/dist/lib/config-loader.d.ts.map +1 -0
- package/dist/lib/config-loader.js +131 -0
- package/dist/lib/config-loader.js.map +1 -0
- package/dist/lib/dependency-graph.d.ts +43 -0
- package/dist/lib/dependency-graph.d.ts.map +1 -0
- package/dist/lib/dependency-graph.js +163 -0
- package/dist/lib/dependency-graph.js.map +1 -0
- package/dist/lib/exit-codes.d.ts +27 -0
- package/dist/lib/exit-codes.d.ts.map +1 -0
- package/dist/lib/exit-codes.js +33 -0
- package/dist/lib/exit-codes.js.map +1 -0
- package/dist/lib/logger.d.ts +39 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +128 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/parallel-runner.d.ts +38 -0
- package/dist/lib/parallel-runner.d.ts.map +1 -0
- package/dist/lib/parallel-runner.js +70 -0
- package/dist/lib/parallel-runner.js.map +1 -0
- package/dist/lib/resource-path.d.ts +205 -0
- package/dist/lib/resource-path.d.ts.map +1 -0
- package/dist/lib/resource-path.js +401 -0
- package/dist/lib/resource-path.js.map +1 -0
- package/dist/lib/resource-uri.d.ts +40 -0
- package/dist/lib/resource-uri.d.ts.map +1 -0
- package/dist/lib/resource-uri.js +86 -0
- package/dist/lib/resource-uri.js.map +1 -0
- package/dist/lib/user-agent.d.ts +2 -0
- package/dist/lib/user-agent.d.ts.map +1 -0
- package/dist/lib/user-agent.js +5 -0
- package/dist/lib/user-agent.js.map +1 -0
- package/dist/models/config.d.ts +83 -0
- package/dist/models/config.d.ts.map +1 -0
- package/dist/models/config.js +6 -0
- package/dist/models/config.js.map +1 -0
- package/dist/models/resource-types.d.ts +66 -0
- package/dist/models/resource-types.d.ts.map +1 -0
- package/dist/models/resource-types.js +243 -0
- package/dist/models/resource-types.js.map +1 -0
- package/dist/models/types.d.ts +47 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +6 -0
- package/dist/models/types.js.map +1 -0
- package/dist/services/api-extractor.d.ts +36 -0
- package/dist/services/api-extractor.d.ts.map +1 -0
- package/dist/services/api-extractor.js +319 -0
- package/dist/services/api-extractor.js.map +1 -0
- package/dist/services/api-publisher.d.ts +18 -0
- package/dist/services/api-publisher.d.ts.map +1 -0
- package/dist/services/api-publisher.js +290 -0
- package/dist/services/api-publisher.js.map +1 -0
- package/dist/services/delete-unmatched-service.d.ts +17 -0
- package/dist/services/delete-unmatched-service.d.ts.map +1 -0
- package/dist/services/delete-unmatched-service.js +143 -0
- package/dist/services/delete-unmatched-service.js.map +1 -0
- package/dist/services/dry-run-reporter.d.ts +30 -0
- package/dist/services/dry-run-reporter.d.ts.map +1 -0
- package/dist/services/dry-run-reporter.js +111 -0
- package/dist/services/dry-run-reporter.js.map +1 -0
- package/dist/services/extract-service.d.ts +47 -0
- package/dist/services/extract-service.d.ts.map +1 -0
- package/dist/services/extract-service.js +374 -0
- package/dist/services/extract-service.js.map +1 -0
- package/dist/services/filter-service.d.ts +29 -0
- package/dist/services/filter-service.d.ts.map +1 -0
- package/dist/services/filter-service.js +143 -0
- package/dist/services/filter-service.js.map +1 -0
- package/dist/services/git-diff-service.d.ts +23 -0
- package/dist/services/git-diff-service.d.ts.map +1 -0
- package/dist/services/git-diff-service.js +135 -0
- package/dist/services/git-diff-service.js.map +1 -0
- package/dist/services/identity-guide-service.d.ts +11 -0
- package/dist/services/identity-guide-service.d.ts.map +1 -0
- package/dist/services/identity-guide-service.js +227 -0
- package/dist/services/identity-guide-service.js.map +1 -0
- package/dist/services/init-service.d.ts +16 -0
- package/dist/services/init-service.d.ts.map +1 -0
- package/dist/services/init-service.js +304 -0
- package/dist/services/init-service.js.map +1 -0
- package/dist/services/keyvault-checker.d.ts +58 -0
- package/dist/services/keyvault-checker.d.ts.map +1 -0
- package/dist/services/keyvault-checker.js +390 -0
- package/dist/services/keyvault-checker.js.map +1 -0
- package/dist/services/override-merger.d.ts +20 -0
- package/dist/services/override-merger.d.ts.map +1 -0
- package/dist/services/override-merger.js +102 -0
- package/dist/services/override-merger.js.map +1 -0
- package/dist/services/product-extractor.d.ts +26 -0
- package/dist/services/product-extractor.d.ts.map +1 -0
- package/dist/services/product-extractor.js +141 -0
- package/dist/services/product-extractor.js.map +1 -0
- package/dist/services/product-publisher.d.ts +15 -0
- package/dist/services/product-publisher.d.ts.map +1 -0
- package/dist/services/product-publisher.js +113 -0
- package/dist/services/product-publisher.js.map +1 -0
- package/dist/services/prompt-service.d.ts +13 -0
- package/dist/services/prompt-service.d.ts.map +1 -0
- package/dist/services/prompt-service.js +69 -0
- package/dist/services/prompt-service.js.map +1 -0
- package/dist/services/publish-service.d.ts +31 -0
- package/dist/services/publish-service.d.ts.map +1 -0
- package/dist/services/publish-service.js +445 -0
- package/dist/services/publish-service.js.map +1 -0
- package/dist/services/resource-extractor.d.ts +52 -0
- package/dist/services/resource-extractor.d.ts.map +1 -0
- package/dist/services/resource-extractor.js +168 -0
- package/dist/services/resource-extractor.js.map +1 -0
- package/dist/services/resource-publisher.d.ts +23 -0
- package/dist/services/resource-publisher.d.ts.map +1 -0
- package/dist/services/resource-publisher.js +349 -0
- package/dist/services/resource-publisher.js.map +1 -0
- package/dist/services/secret-redactor.d.ts +20 -0
- package/dist/services/secret-redactor.d.ts.map +1 -0
- package/dist/services/secret-redactor.js +45 -0
- package/dist/services/secret-redactor.js.map +1 -0
- package/dist/services/transitive-resolver.d.ts +45 -0
- package/dist/services/transitive-resolver.d.ts.map +1 -0
- package/dist/services/transitive-resolver.js +177 -0
- package/dist/services/transitive-resolver.js.map +1 -0
- package/dist/services/workspace-extractor.d.ts +34 -0
- package/dist/services/workspace-extractor.d.ts.map +1 -0
- package/dist/services/workspace-extractor.js +120 -0
- package/dist/services/workspace-extractor.js.map +1 -0
- package/dist/templates/azure-devops/extract-pipeline.d.ts +9 -0
- package/dist/templates/azure-devops/extract-pipeline.d.ts.map +1 -0
- package/dist/templates/azure-devops/extract-pipeline.js +95 -0
- package/dist/templates/azure-devops/extract-pipeline.js.map +1 -0
- package/dist/templates/azure-devops/publish-pipeline.d.ts +10 -0
- package/dist/templates/azure-devops/publish-pipeline.d.ts.map +1 -0
- package/dist/templates/azure-devops/publish-pipeline.js +100 -0
- package/dist/templates/azure-devops/publish-pipeline.js.map +1 -0
- package/dist/templates/configs/filter-config.d.ts +6 -0
- package/dist/templates/configs/filter-config.d.ts.map +1 -0
- package/dist/templates/configs/filter-config.js +51 -0
- package/dist/templates/configs/filter-config.js.map +1 -0
- package/dist/templates/configs/override-config.d.ts +6 -0
- package/dist/templates/configs/override-config.d.ts.map +1 -0
- package/dist/templates/configs/override-config.js +45 -0
- package/dist/templates/configs/override-config.js.map +1 -0
- package/dist/templates/configs/package-json.d.ts +10 -0
- package/dist/templates/configs/package-json.d.ts.map +1 -0
- package/dist/templates/configs/package-json.js +19 -0
- package/dist/templates/configs/package-json.js.map +1 -0
- package/dist/templates/copilot/identity-setup-prompt.d.ts +13 -0
- package/dist/templates/copilot/identity-setup-prompt.d.ts.map +1 -0
- package/dist/templates/copilot/identity-setup-prompt.js +279 -0
- package/dist/templates/copilot/identity-setup-prompt.js.map +1 -0
- package/dist/templates/github-actions/extract-workflow.d.ts +9 -0
- package/dist/templates/github-actions/extract-workflow.d.ts.map +1 -0
- package/dist/templates/github-actions/extract-workflow.js +126 -0
- package/dist/templates/github-actions/extract-workflow.js.map +1 -0
- package/dist/templates/github-actions/publish-workflow.d.ts +10 -0
- package/dist/templates/github-actions/publish-workflow.d.ts.map +1 -0
- package/dist/templates/github-actions/publish-workflow.js +105 -0
- package/dist/templates/github-actions/publish-workflow.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T021: Resource type extractor
|
|
3
|
+
* Generic extract logic: list resources via IApimClient, write each to IArtifactStore.
|
|
4
|
+
* Handles all 33 types using ResourceType metadata. Preserves opaque JSON per FR-009.
|
|
5
|
+
*/
|
|
6
|
+
import { RESOURCE_TYPE_METADATA } from '../models/resource-types.js';
|
|
7
|
+
import { redactSecrets } from './secret-redactor.js';
|
|
8
|
+
import { shouldIncludeResource } from './filter-service.js';
|
|
9
|
+
import { logger } from '../lib/logger.js';
|
|
10
|
+
import { buildResourceLabel } from '../lib/resource-uri.js';
|
|
11
|
+
/**
|
|
12
|
+
* Check if a resource type's LIST endpoint returns shallow data that omits
|
|
13
|
+
* fields required for round-trip publish. When true, extraction must issue
|
|
14
|
+
* an individual GET per item to fetch the complete resource.
|
|
15
|
+
*/
|
|
16
|
+
function typeNeedsFullFetch(type) {
|
|
17
|
+
return RESOURCE_TYPE_METADATA[type].listOmitsFields === true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract the ARM resource name from a raw JSON object.
|
|
21
|
+
* ARM resources have a 'name' field at the top level.
|
|
22
|
+
*/
|
|
23
|
+
export function extractResourceName(json) {
|
|
24
|
+
const name = json.name;
|
|
25
|
+
if (!name) {
|
|
26
|
+
throw new Error('Resource JSON missing required "name" field');
|
|
27
|
+
}
|
|
28
|
+
return name;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Extract all resources of a given type from APIM and write to artifact store.
|
|
32
|
+
*
|
|
33
|
+
* @param client - APIM REST client
|
|
34
|
+
* @param store - Artifact file store
|
|
35
|
+
* @param context - APIM service context
|
|
36
|
+
* @param type - Resource type to extract
|
|
37
|
+
* @param outputDir - Output directory
|
|
38
|
+
* @param filter - Optional filter config
|
|
39
|
+
* @param parent - Parent descriptor for child resources
|
|
40
|
+
* @param workspace - Optional workspace name
|
|
41
|
+
* @returns Extraction result
|
|
42
|
+
*/
|
|
43
|
+
export async function extractResourceType(client, store, context, type, outputDir, filter, parent, workspace) {
|
|
44
|
+
const result = {
|
|
45
|
+
type,
|
|
46
|
+
extracted: [],
|
|
47
|
+
totalCount: 0,
|
|
48
|
+
errorCount: 0,
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
const resources = client.listResources(context, type, parent);
|
|
52
|
+
for await (const listJson of resources) {
|
|
53
|
+
result.totalCount++;
|
|
54
|
+
let descriptor;
|
|
55
|
+
try {
|
|
56
|
+
const name = extractResourceName(listJson);
|
|
57
|
+
descriptor = buildDescriptor(type, name, parent, workspace);
|
|
58
|
+
// Apply filter
|
|
59
|
+
if (!shouldIncludeResource(descriptor, filter)) {
|
|
60
|
+
logger.debug(`Filtered out ${buildResourceLabel(descriptor)}`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Some APIM list endpoints return a shallow response that omits the
|
|
64
|
+
// heavyweight payload we need for round-trip publish. ApiSchema list
|
|
65
|
+
// omits `properties.document` (the GraphQL SDL / XSD / JSON-schema
|
|
66
|
+
// body); an individual GET returns it. Fetch the full resource so the
|
|
67
|
+
// extract captures what publish requires. Falls back to the list
|
|
68
|
+
// payload if the GET returns undefined (shouldn't normally happen —
|
|
69
|
+
// we just listed it).
|
|
70
|
+
let json = listJson;
|
|
71
|
+
if (typeNeedsFullFetch(type)) {
|
|
72
|
+
const full = await client.getResource(context, descriptor);
|
|
73
|
+
if (full) {
|
|
74
|
+
json = full;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Apply secret redaction
|
|
78
|
+
const safeJson = redactSecrets(descriptor, json);
|
|
79
|
+
// Write to artifact store (preserves opaque JSON per FR-009)
|
|
80
|
+
await store.writeResource(outputDir, descriptor, safeJson);
|
|
81
|
+
result.extracted.push({
|
|
82
|
+
descriptor,
|
|
83
|
+
json: safeJson,
|
|
84
|
+
status: 'success',
|
|
85
|
+
});
|
|
86
|
+
logger.info(`Extracted ${buildResourceLabel(descriptor)}`);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
result.errorCount++;
|
|
90
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
91
|
+
if (descriptor) {
|
|
92
|
+
logger.error(`Failed to extract ${buildResourceLabel(descriptor)}: ${errorMessage}`);
|
|
93
|
+
result.extracted.push({
|
|
94
|
+
descriptor,
|
|
95
|
+
json: {},
|
|
96
|
+
status: 'error',
|
|
97
|
+
error: errorMessage,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
logger.error(`Failed to extract ${type} resource: ${errorMessage}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
108
|
+
logger.error(`Failed to list ${type}: ${errorMessage}`);
|
|
109
|
+
result.errorCount++;
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Extract a single resource by descriptor and write to artifact store.
|
|
115
|
+
*/
|
|
116
|
+
export async function extractSingleResource(client, store, context, descriptor, outputDir) {
|
|
117
|
+
try {
|
|
118
|
+
const json = await client.getResource(context, descriptor);
|
|
119
|
+
if (!json) {
|
|
120
|
+
return {
|
|
121
|
+
descriptor,
|
|
122
|
+
json: {},
|
|
123
|
+
status: 'error',
|
|
124
|
+
error: `Resource not found: ${buildResourceLabel(descriptor)}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Apply secret redaction
|
|
128
|
+
const safeJson = redactSecrets(descriptor, json);
|
|
129
|
+
// Write to artifact store
|
|
130
|
+
await store.writeResource(outputDir, descriptor, safeJson);
|
|
131
|
+
logger.info(`Extracted ${buildResourceLabel(descriptor)}`);
|
|
132
|
+
return { descriptor, json: safeJson, status: 'success' };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
136
|
+
logger.error(`Failed to extract ${buildResourceLabel(descriptor)}: ${errorMessage}`);
|
|
137
|
+
return { descriptor, json: {}, status: 'error', error: errorMessage };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build a ResourceDescriptor for a given type, name, optional parent, and workspace.
|
|
142
|
+
*
|
|
143
|
+
* The nameParts array is derived generically using the count of positional
|
|
144
|
+
* placeholders in armPathSuffix:
|
|
145
|
+
* - placeholderCount === parent.nameParts.length → singleton child (policy, wiki):
|
|
146
|
+
* nameParts = parent.nameParts (own fixed name not encoded in path)
|
|
147
|
+
* - placeholderCount > parent.nameParts.length → named child:
|
|
148
|
+
* nameParts = [...parent.nameParts, name]
|
|
149
|
+
* - no parent → top-level: nameParts = [name] (or [] for zero-placeholder types)
|
|
150
|
+
*/
|
|
151
|
+
function buildDescriptor(type, name, parent, workspace) {
|
|
152
|
+
const metadata = RESOURCE_TYPE_METADATA[type];
|
|
153
|
+
const placeholderCount = (metadata.armPathSuffix.match(/\{\d+\}/g) ?? []).length;
|
|
154
|
+
let nameParts;
|
|
155
|
+
if (!parent) {
|
|
156
|
+
nameParts = placeholderCount === 0 ? [] : [name];
|
|
157
|
+
}
|
|
158
|
+
else if (parent.nameParts.length >= placeholderCount) {
|
|
159
|
+
// Singleton child (policy, wiki): identified solely by parent's name-parts
|
|
160
|
+
nameParts = [...parent.nameParts];
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Named child: has its own distinct position in the ARM path
|
|
164
|
+
nameParts = [...parent.nameParts, name];
|
|
165
|
+
}
|
|
166
|
+
return { type, nameParts, workspace };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=resource-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-extractor.js","sourceRoot":"","sources":["../../src/services/resource-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T031: Generic resource publisher
|
|
3
|
+
* Read resource from IArtifactStore, apply overrides, PUT via IApimClient.
|
|
4
|
+
* Handles all 33 resource types using ResourceType metadata.
|
|
5
|
+
* MUST preserve opaque JSON per FR-009.
|
|
6
|
+
*/
|
|
7
|
+
import type { IApimClient } from '../clients/iapim-client.js';
|
|
8
|
+
import type { IArtifactStore } from '../clients/iartifact-store.js';
|
|
9
|
+
import type { ApimServiceContext, ResourceDescriptor } from '../models/types.js';
|
|
10
|
+
import type { PublishConfig } from '../models/config.js';
|
|
11
|
+
export interface ResourcePublishResult {
|
|
12
|
+
descriptor: ResourceDescriptor;
|
|
13
|
+
status: 'success' | 'failed' | 'skipped';
|
|
14
|
+
action: 'put' | 'delete' | 'noop';
|
|
15
|
+
error?: Error;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Publish a single resource: read from store, apply overrides, PUT to APIM.
|
|
19
|
+
* Preserves opaque JSON (FR-009). Uses applyOverrides for env-specific values.
|
|
20
|
+
* Returns 'skipped' if resource file doesn't exist in store.
|
|
21
|
+
*/
|
|
22
|
+
export declare function publishResource(client: IApimClient, store: IArtifactStore, context: ApimServiceContext, descriptor: ResourceDescriptor, config: PublishConfig): Promise<ResourcePublishResult>;
|
|
23
|
+
//# sourceMappingURL=resource-publisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-publisher.d.ts","sourceRoot":"","sources":["../../src/services/resource-publisher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAyCD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAoIhC"}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T031: Generic resource publisher
|
|
3
|
+
* Read resource from IArtifactStore, apply overrides, PUT via IApimClient.
|
|
4
|
+
* Handles all 33 resource types using ResourceType metadata.
|
|
5
|
+
* MUST preserve opaque JSON per FR-009.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceType } from '../models/resource-types.js';
|
|
8
|
+
import { applyOverrides } from './override-merger.js';
|
|
9
|
+
import { checkKeyVaultSecretAccess } from './keyvault-checker.js';
|
|
10
|
+
import { getNamePart } from '../lib/resource-path.js';
|
|
11
|
+
/**
|
|
12
|
+
* Policy resource types that have external XML content
|
|
13
|
+
*/
|
|
14
|
+
const POLICY_TYPES = new Set([
|
|
15
|
+
ResourceType.ServicePolicy,
|
|
16
|
+
ResourceType.ProductPolicy,
|
|
17
|
+
ResourceType.ApiPolicy,
|
|
18
|
+
ResourceType.ApiOperationPolicy,
|
|
19
|
+
ResourceType.GraphQLResolverPolicy,
|
|
20
|
+
]);
|
|
21
|
+
/**
|
|
22
|
+
* Association resource types that read from association files
|
|
23
|
+
*/
|
|
24
|
+
const ASSOCIATION_TYPES = new Map([
|
|
25
|
+
[ResourceType.ProductApi, 'apis'],
|
|
26
|
+
[ResourceType.ProductGroup, 'groups'],
|
|
27
|
+
[ResourceType.GatewayApi, 'apis'],
|
|
28
|
+
]);
|
|
29
|
+
/**
|
|
30
|
+
* Maps association resource types to their parent resource types.
|
|
31
|
+
* readAssociation / buildAssociationFilePath expect a Product or Gateway
|
|
32
|
+
* descriptor, not the association child type itself.
|
|
33
|
+
*/
|
|
34
|
+
const ASSOCIATION_PARENT_TYPES = new Map([
|
|
35
|
+
[ResourceType.ProductApi, ResourceType.Product],
|
|
36
|
+
[ResourceType.ProductGroup, ResourceType.Product],
|
|
37
|
+
[ResourceType.GatewayApi, ResourceType.Gateway],
|
|
38
|
+
]);
|
|
39
|
+
/**
|
|
40
|
+
* Wiki resource types that read markdown content
|
|
41
|
+
*/
|
|
42
|
+
const WIKI_TYPES = new Set([
|
|
43
|
+
ResourceType.ApiWiki,
|
|
44
|
+
ResourceType.ProductWiki,
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Publish a single resource: read from store, apply overrides, PUT to APIM.
|
|
48
|
+
* Preserves opaque JSON (FR-009). Uses applyOverrides for env-specific values.
|
|
49
|
+
* Returns 'skipped' if resource file doesn't exist in store.
|
|
50
|
+
*/
|
|
51
|
+
export async function publishResource(client, store, context, descriptor, config) {
|
|
52
|
+
try {
|
|
53
|
+
// Handle association types (ProductApi, ProductGroup, GatewayApi)
|
|
54
|
+
const associationType = ASSOCIATION_TYPES.get(descriptor.type);
|
|
55
|
+
if (associationType) {
|
|
56
|
+
return await publishAssociation(client, store, context, descriptor, config, associationType);
|
|
57
|
+
}
|
|
58
|
+
// Handle wiki types
|
|
59
|
+
if (WIKI_TYPES.has(descriptor.type)) {
|
|
60
|
+
return await publishWiki(client, store, context, descriptor, config);
|
|
61
|
+
}
|
|
62
|
+
// Handle policy types — artifact is policy.xml (raw XML), not a JSON info file
|
|
63
|
+
if (POLICY_TYPES.has(descriptor.type)) {
|
|
64
|
+
return await publishPolicy(client, store, context, descriptor, config);
|
|
65
|
+
}
|
|
66
|
+
// Read resource JSON from store
|
|
67
|
+
let json = await store.readResource(config.sourceDir, descriptor);
|
|
68
|
+
if (!json) {
|
|
69
|
+
return {
|
|
70
|
+
descriptor,
|
|
71
|
+
status: 'skipped',
|
|
72
|
+
action: 'noop',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Apply overrides (deep merge, preserves opaque structure)
|
|
76
|
+
json = applyOverrides(descriptor, json, config.overrides);
|
|
77
|
+
// For KeyVault-backed NamedValues:
|
|
78
|
+
// 1. Strip properties.value — APIM must not receive both keyVault and value
|
|
79
|
+
// in the same PUT body, as it causes indefinite provisioning or rejection.
|
|
80
|
+
// 2. Pre-flight access check — verify the managed identity has GET access
|
|
81
|
+
// to the secret before attempting the PUT. Surfaces permission errors
|
|
82
|
+
// early and fails fast instead of polling until timeout.
|
|
83
|
+
if (descriptor.type === ResourceType.NamedValue) {
|
|
84
|
+
const props = json.properties;
|
|
85
|
+
const kvBlock = props?.keyVault;
|
|
86
|
+
if (kvBlock != null) {
|
|
87
|
+
const { value: _omit, ...propsWithoutValue } = props;
|
|
88
|
+
json = { ...json, properties: propsWithoutValue };
|
|
89
|
+
const secretIdentifier = kvBlock.secretIdentifier;
|
|
90
|
+
const identityClientId = kvBlock.identityClientId;
|
|
91
|
+
if (secretIdentifier) {
|
|
92
|
+
await checkKeyVaultSecretAccess(secretIdentifier, identityClientId, {
|
|
93
|
+
subscriptionId: context.subscriptionId,
|
|
94
|
+
resourceGroup: context.resourceGroup,
|
|
95
|
+
serviceName: context.serviceName,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Normalize Subscription scope.
|
|
101
|
+
// APIM stores scope as a full ARM resource path (e.g.
|
|
102
|
+
// "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{svc}/apis")
|
|
103
|
+
// but the PUT endpoint requires a relative APIM path (e.g. "/apis" or
|
|
104
|
+
// "/apis/{apiId}" or "/products/{productId}"). Strip the service base path
|
|
105
|
+
// prefix when it is present so that the publish round-trip works correctly.
|
|
106
|
+
// HOWEVER: Skip publishing subscriptions with root-level scope (e.g. the built-in
|
|
107
|
+
// "master" subscription), as APIM treats these as read-only system resources
|
|
108
|
+
// and returns ValidationError when attempting to update them.
|
|
109
|
+
if (descriptor.type === ResourceType.Subscription) {
|
|
110
|
+
const props = json.properties;
|
|
111
|
+
const scope = props?.scope;
|
|
112
|
+
// Built-in master subscription has scope ending with the service path (no /apis or /products suffix)
|
|
113
|
+
// Skip it since APIM doesn't allow updates to built-in subscriptions
|
|
114
|
+
if (scope && (scope.endsWith('/') || (!scope.includes('/apis') && !scope.includes('/products')))) {
|
|
115
|
+
return {
|
|
116
|
+
descriptor,
|
|
117
|
+
status: 'skipped',
|
|
118
|
+
action: 'noop',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
json = normalizeSubscriptionScope(json, context);
|
|
122
|
+
}
|
|
123
|
+
// ApiRelease: normalize properties.apiId from source ARM path to target ARM path.
|
|
124
|
+
// APIM stores apiId as the full source ARM resource path, e.g.:
|
|
125
|
+
// /subscriptions/{src-sub}/resourceGroups/{src-rg}/providers/
|
|
126
|
+
// Microsoft.ApiManagement/service/{src-svc}/apis/my-api;rev=2
|
|
127
|
+
// The PUT to the target service must reference the target service path,
|
|
128
|
+
// otherwise APIM rejects the request with a validation error.
|
|
129
|
+
if (descriptor.type === ResourceType.ApiRelease) {
|
|
130
|
+
json = normalizeApiReleaseApiId(json, context);
|
|
131
|
+
}
|
|
132
|
+
// API Revisions (e.g., "my-api;rev=2") need sourceApiId so APIM knows which
|
|
133
|
+
// base API to copy structure from. Also strip null properties that cause
|
|
134
|
+
// validation errors in APIM's revision creation.
|
|
135
|
+
if (descriptor.type === ResourceType.Api) {
|
|
136
|
+
const apiName = getNamePart(descriptor.nameParts, 0);
|
|
137
|
+
if (apiName.includes(';rev=')) {
|
|
138
|
+
const baseApiName = apiName.split(';rev=')[0];
|
|
139
|
+
const props = json.properties;
|
|
140
|
+
const cleanProps = {};
|
|
141
|
+
for (const [key, val] of Object.entries(props ?? {})) {
|
|
142
|
+
if (val !== null)
|
|
143
|
+
cleanProps[key] = val;
|
|
144
|
+
}
|
|
145
|
+
cleanProps.sourceApiId = `/subscriptions/${context.subscriptionId}/resourceGroups/${context.resourceGroup}/providers/Microsoft.ApiManagement/service/${context.serviceName}/apis/${baseApiName}`;
|
|
146
|
+
json = { ...json, properties: cleanProps };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// PUT to APIM
|
|
150
|
+
await client.putResource(context, descriptor, json);
|
|
151
|
+
return {
|
|
152
|
+
descriptor,
|
|
153
|
+
status: 'success',
|
|
154
|
+
action: 'put',
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
descriptor,
|
|
160
|
+
status: 'failed',
|
|
161
|
+
action: 'noop',
|
|
162
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Publish association resource (ProductApi, ProductGroup, GatewayApi)
|
|
168
|
+
*/
|
|
169
|
+
async function publishAssociation(client, store, context, descriptor, config, associationType) {
|
|
170
|
+
try {
|
|
171
|
+
// buildAssociationFilePath (called inside readAssociation) only accepts
|
|
172
|
+
// Product or Gateway descriptors — not the association child types
|
|
173
|
+
// (ProductApi, ProductGroup, GatewayApi). Derive the parent descriptor.
|
|
174
|
+
const parentType = ASSOCIATION_PARENT_TYPES.get(descriptor.type);
|
|
175
|
+
const parentDescriptor = {
|
|
176
|
+
type: parentType,
|
|
177
|
+
nameParts: [getNamePart(descriptor.nameParts, 0)],
|
|
178
|
+
workspace: descriptor.workspace,
|
|
179
|
+
};
|
|
180
|
+
const names = await store.readAssociation(config.sourceDir, parentDescriptor, associationType);
|
|
181
|
+
// Create association for each name
|
|
182
|
+
for (const name of names) {
|
|
183
|
+
const assocDescriptor = {
|
|
184
|
+
type: descriptor.type,
|
|
185
|
+
nameParts: [getNamePart(descriptor.nameParts, 0), name],
|
|
186
|
+
};
|
|
187
|
+
// PUT empty body for association (APIM uses PUT to create association)
|
|
188
|
+
await client.putResource(context, assocDescriptor, {});
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
descriptor,
|
|
192
|
+
status: 'success',
|
|
193
|
+
action: 'put',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
return {
|
|
198
|
+
descriptor,
|
|
199
|
+
status: 'failed',
|
|
200
|
+
action: 'noop',
|
|
201
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Publish wiki resource (ApiWiki, ProductWiki)
|
|
207
|
+
*/
|
|
208
|
+
async function publishWiki(client, store, context, descriptor, config) {
|
|
209
|
+
try {
|
|
210
|
+
const wikiContent = await store.readContent(config.sourceDir, descriptor, 'specification');
|
|
211
|
+
if (!wikiContent) {
|
|
212
|
+
return {
|
|
213
|
+
descriptor,
|
|
214
|
+
status: 'skipped',
|
|
215
|
+
action: 'noop',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// Build wiki payload
|
|
219
|
+
const payload = {
|
|
220
|
+
properties: {
|
|
221
|
+
documents: [
|
|
222
|
+
{
|
|
223
|
+
documentId: 'default',
|
|
224
|
+
content: wikiContent.content,
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
await client.putResource(context, descriptor, payload);
|
|
230
|
+
return {
|
|
231
|
+
descriptor,
|
|
232
|
+
status: 'success',
|
|
233
|
+
action: 'put',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
return {
|
|
238
|
+
descriptor,
|
|
239
|
+
status: 'failed',
|
|
240
|
+
action: 'noop',
|
|
241
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Publish policy resource (ServicePolicy, ApiPolicy, ProductPolicy, ApiOperationPolicy,
|
|
247
|
+
* GraphQLResolverPolicy). The artifact on disk is a raw policy.xml file; there is no
|
|
248
|
+
* separate JSON info file for these types. Reads the XML and PUTs it with format=rawxml.
|
|
249
|
+
*/
|
|
250
|
+
async function publishPolicy(client, store, context, descriptor, config) {
|
|
251
|
+
try {
|
|
252
|
+
const policyContent = await store.readContent(config.sourceDir, descriptor, 'policy');
|
|
253
|
+
if (!policyContent) {
|
|
254
|
+
return {
|
|
255
|
+
descriptor,
|
|
256
|
+
status: 'skipped',
|
|
257
|
+
action: 'noop',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const payload = {
|
|
261
|
+
properties: {
|
|
262
|
+
value: policyContent.content,
|
|
263
|
+
format: 'rawxml',
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
await client.putResource(context, descriptor, payload);
|
|
267
|
+
return {
|
|
268
|
+
descriptor,
|
|
269
|
+
status: 'success',
|
|
270
|
+
action: 'put',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
return {
|
|
275
|
+
descriptor,
|
|
276
|
+
status: 'failed',
|
|
277
|
+
action: 'noop',
|
|
278
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Normalise the `properties.scope` field of a Subscription resource.
|
|
284
|
+
*
|
|
285
|
+
* APIM returns scope as a full ARM resource path on GET, e.g.:
|
|
286
|
+
* /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{svc}/apis
|
|
287
|
+
*
|
|
288
|
+
* The PUT endpoint requires a relative APIM path, e.g.:
|
|
289
|
+
* /apis
|
|
290
|
+
* /apis/{apiId}
|
|
291
|
+
* /products/{productId}
|
|
292
|
+
*
|
|
293
|
+
* Strip the service base ARM path prefix when it is present so that the
|
|
294
|
+
* extract → publish round-trip works without manual edits.
|
|
295
|
+
*/
|
|
296
|
+
function normalizeSubscriptionScope(json, context) {
|
|
297
|
+
const props = json.properties;
|
|
298
|
+
if (!props)
|
|
299
|
+
return json;
|
|
300
|
+
const scope = props.scope;
|
|
301
|
+
if (!scope)
|
|
302
|
+
return json;
|
|
303
|
+
// The ARM path prefix lives between "management.azure.com" and the first
|
|
304
|
+
// APIM-relative segment. Derive it from baseUrl by stripping the protocol+host.
|
|
305
|
+
const armPathPrefix = context.baseUrl.replace(/^https?:\/\/[^/]+/, '');
|
|
306
|
+
if (scope.startsWith(armPathPrefix)) {
|
|
307
|
+
const relativeScope = scope.slice(armPathPrefix.length) || '/';
|
|
308
|
+
return {
|
|
309
|
+
...json,
|
|
310
|
+
properties: { ...props, scope: relativeScope },
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return json;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Normalise the `properties.apiId` field of an ApiRelease resource.
|
|
317
|
+
*
|
|
318
|
+
* APIM returns apiId as a full ARM resource path that includes the source
|
|
319
|
+
* service coordinates, e.g.:
|
|
320
|
+
* /subscriptions/{src-sub}/resourceGroups/{src-rg}/providers/
|
|
321
|
+
* Microsoft.ApiManagement/service/{src-svc}/apis/my-api;rev=2
|
|
322
|
+
*
|
|
323
|
+
* The PUT to the target service must reference the TARGET service path;
|
|
324
|
+
* otherwise APIM rejects the request because the apiId does not match the
|
|
325
|
+
* service being written to. Rebuild the apiId using the target ARM prefix
|
|
326
|
+
* derived from the publish context.
|
|
327
|
+
*/
|
|
328
|
+
function normalizeApiReleaseApiId(json, context) {
|
|
329
|
+
const props = json.properties;
|
|
330
|
+
if (!props)
|
|
331
|
+
return json;
|
|
332
|
+
const apiId = props.apiId;
|
|
333
|
+
if (!apiId)
|
|
334
|
+
return json;
|
|
335
|
+
// The target ARM prefix for this service (no https:// host, no trailing slash)
|
|
336
|
+
const targetArmPrefix = context.baseUrl.replace(/^https?:\/\/[^/]+/, '');
|
|
337
|
+
// Extract the /apis/... suffix and rebuild with the target ARM prefix.
|
|
338
|
+
// The suffix includes the full API name, e.g. "/apis/my-api;rev=2".
|
|
339
|
+
const apisIndex = apiId.indexOf('/apis/');
|
|
340
|
+
if (apisIndex !== -1) {
|
|
341
|
+
const apisSuffix = apiId.slice(apisIndex);
|
|
342
|
+
return {
|
|
343
|
+
...json,
|
|
344
|
+
properties: { ...props, apiId: targetArmPrefix + apisSuffix },
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return json;
|
|
348
|
+
}
|
|
349
|
+
//# sourceMappingURL=resource-publisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-publisher.js","sourceRoot":"","sources":["../../src/services/resource-publisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T026: Secret redaction service
|
|
3
|
+
* Detect properties.secret === true on named values,
|
|
4
|
+
* replace properties.value with redaction marker.
|
|
5
|
+
*/
|
|
6
|
+
import { ResourceDescriptor } from '../models/types.js';
|
|
7
|
+
/** Marker used to replace secret values in extracted artifacts */
|
|
8
|
+
export declare const REDACTION_MARKER = "*** REDACTED ***";
|
|
9
|
+
/**
|
|
10
|
+
* Redact secret values from a resource's JSON payload.
|
|
11
|
+
*
|
|
12
|
+
* Only applies to NamedValue resources with properties.secret === true.
|
|
13
|
+
* KeyVault references are preserved as-is (the URL is not itself secret).
|
|
14
|
+
*
|
|
15
|
+
* @param descriptor - Resource descriptor
|
|
16
|
+
* @param json - Raw resource JSON (will be cloned, not mutated)
|
|
17
|
+
* @returns JSON with secrets redacted
|
|
18
|
+
*/
|
|
19
|
+
export declare function redactSecrets(descriptor: ResourceDescriptor, json: Record<string, unknown>): Record<string, unknown>;
|
|
20
|
+
//# sourceMappingURL=secret-redactor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redactor.d.ts","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAIxD,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,kBAAkB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T026: Secret redaction service
|
|
3
|
+
* Detect properties.secret === true on named values,
|
|
4
|
+
* replace properties.value with redaction marker.
|
|
5
|
+
*/
|
|
6
|
+
import { ResourceType } from '../models/resource-types.js';
|
|
7
|
+
import { logger } from '../lib/logger.js';
|
|
8
|
+
import { getNamePart } from '../lib/resource-path.js';
|
|
9
|
+
/** Marker used to replace secret values in extracted artifacts */
|
|
10
|
+
export const REDACTION_MARKER = '*** REDACTED ***';
|
|
11
|
+
/**
|
|
12
|
+
* Redact secret values from a resource's JSON payload.
|
|
13
|
+
*
|
|
14
|
+
* Only applies to NamedValue resources with properties.secret === true.
|
|
15
|
+
* KeyVault references are preserved as-is (the URL is not itself secret).
|
|
16
|
+
*
|
|
17
|
+
* @param descriptor - Resource descriptor
|
|
18
|
+
* @param json - Raw resource JSON (will be cloned, not mutated)
|
|
19
|
+
* @returns JSON with secrets redacted
|
|
20
|
+
*/
|
|
21
|
+
export function redactSecrets(descriptor, json) {
|
|
22
|
+
if (descriptor.type !== ResourceType.NamedValue) {
|
|
23
|
+
return json;
|
|
24
|
+
}
|
|
25
|
+
const properties = json.properties;
|
|
26
|
+
if (!properties) {
|
|
27
|
+
return json;
|
|
28
|
+
}
|
|
29
|
+
// Only redact if explicitly marked as secret
|
|
30
|
+
if (properties.secret !== true) {
|
|
31
|
+
return json;
|
|
32
|
+
}
|
|
33
|
+
// Skip KeyVault-backed named values — the keyVault block is not secret
|
|
34
|
+
if (properties.keyVault !== undefined && properties.keyVault !== null) {
|
|
35
|
+
logger.debug(`Named value "${getNamePart(descriptor.nameParts, 0)}" is KeyVault-backed, preserving reference`);
|
|
36
|
+
return json;
|
|
37
|
+
}
|
|
38
|
+
// Deep clone to avoid mutating the original
|
|
39
|
+
const redacted = JSON.parse(JSON.stringify(json));
|
|
40
|
+
const redactedProps = redacted.properties;
|
|
41
|
+
redactedProps.value = REDACTION_MARKER;
|
|
42
|
+
logger.debug(`Redacted secret value for named value "${getNamePart(descriptor.nameParts, 0)}"`);
|
|
43
|
+
return redacted;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=secret-redactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redactor.js","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,UAA8B,EAC9B,IAA6B;IAE7B,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,CAAC,UAAU,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAiD,CAAC;IAC1E,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6CAA6C;IAC7C,IAAI,UAAU,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IACvE,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,CAAC,KAAK,CAAC,gBAAgB,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,4CAA4C,CAAC,CAAC;QAC/G,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAA4B,CAAC;IAC7E,MAAM,aAAa,GAAG,QAAQ,CAAC,UAAqC,CAAC;IACrE,aAAa,CAAC,KAAK,GAAG,gBAAgB,CAAC;IAEvC,MAAM,CAAC,KAAK,CAAC,0CAA0C,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAChG,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|