@adobe/helix-config 1.3.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/package.json +6 -2
- package/src/ConfigContext.d.ts +2 -2
- package/src/ConfigContext.js +3 -3
- package/src/S3Loader.d.ts +32 -0
- package/src/config-legacy.js +11 -10
- package/src/config-view.js +46 -24
- package/src/domain-view.js +10 -10
- package/src/index.d.ts +3 -0
- package/src/HelixStorage.ts +0 -47
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# [2.1.0](https://github.com/adobe/helix-config/compare/v2.0.0...v2.1.0) (2024-03-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* return 404 for missing branch ([#33](https://github.com/adobe/helix-config/issues/33)) ([b3ea0f4](https://github.com/adobe/helix-config/commit/b3ea0f419f34ee0a94b02b9a67c06c64eec06d2d))
|
|
7
|
+
|
|
8
|
+
# [2.0.0](https://github.com/adobe/helix-config/compare/v1.3.3...v2.0.0) (2024-03-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* refactor s3 loader abstraction ([#31](https://github.com/adobe/helix-config/issues/31)) ([086cce5](https://github.com/adobe/helix-config/commit/086cce51e76da932ed68acef095e262db0e50be5))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
* replace HelixStorage with S3Loader
|
|
19
|
+
|
|
1
20
|
## [1.3.3](https://github.com/adobe/helix-config/compare/v1.3.2...v1.3.3) (2024-03-14)
|
|
2
21
|
|
|
3
22
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/helix-config",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Helix Config",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"scripts": {
|
|
8
12
|
"test": "c8 mocha",
|
|
@@ -32,7 +36,7 @@
|
|
|
32
36
|
"@adobe/eslint-config-helix": "2.0.6",
|
|
33
37
|
"@semantic-release/changelog": "6.0.3",
|
|
34
38
|
"@semantic-release/git": "10.0.1",
|
|
35
|
-
"@semantic-release/npm": "
|
|
39
|
+
"@semantic-release/npm": "12.0.0",
|
|
36
40
|
"c8": "9.1.0",
|
|
37
41
|
"eslint": "8.57.0",
|
|
38
42
|
"husky": "9.0.11",
|
package/src/ConfigContext.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* OF ANY KIND; either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import {S3Loader} from "./S3Loader";
|
|
13
13
|
|
|
14
14
|
type Fetch = (url: string|Request, options?: RequestOptions) => Promise<Response>;
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ export declare enum ConfigScope {
|
|
|
23
23
|
export declare class ConfigContext {
|
|
24
24
|
withLog(log: Console): ConfigContext;
|
|
25
25
|
withEnv(env: object): ConfigContext;
|
|
26
|
-
|
|
26
|
+
withS3Loader(loader: S3Loader): ConfigContext;
|
|
27
27
|
withFetch(fetch: Fetch): ConfigContext;
|
|
28
28
|
}
|
|
29
29
|
|
package/src/ConfigContext.js
CHANGED
|
@@ -45,7 +45,7 @@ export class ConfigContext {
|
|
|
45
45
|
|
|
46
46
|
env = {};
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
loader = null;
|
|
49
49
|
|
|
50
50
|
fetch = null;
|
|
51
51
|
|
|
@@ -59,8 +59,8 @@ export class ConfigContext {
|
|
|
59
59
|
return this;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
this.
|
|
62
|
+
withS3Loader(loader) {
|
|
63
|
+
this.loader = loader;
|
|
64
64
|
return this;
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License; Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing; software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS; WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND; either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
import {PipelineResponse} from "./PipelineResponse";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* S3 Loader abstration used to load S3 objects.
|
|
17
|
+
*/
|
|
18
|
+
export declare interface S3Loader {
|
|
19
|
+
/**
|
|
20
|
+
* Loads a S3 object from the given bucket and key.
|
|
21
|
+
* @param {string} bucketId
|
|
22
|
+
* @param {string} key
|
|
23
|
+
*/
|
|
24
|
+
getObject(bucketId, key): Promise<PipelineResponse>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves the head metadata of a S3 object from the given bucket and key.
|
|
28
|
+
* @param {string} bucketId
|
|
29
|
+
* @param {string} key
|
|
30
|
+
*/
|
|
31
|
+
headObject(bucketId, key): Promise<PipelineResponse>;
|
|
32
|
+
}
|
package/src/config-legacy.js
CHANGED
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { SCOPE_PIPELINE } from './ConfigContext.js';
|
|
13
13
|
|
|
14
|
+
const HELIX_CODE_BUS = 'helix-code-bus';
|
|
15
|
+
|
|
16
|
+
const HELIX_CONTENT_BUS = 'helix-content-bus';
|
|
17
|
+
|
|
14
18
|
/**
|
|
15
19
|
* Retrieves the helix-config.json which is an aggregate from fstab.yaml and head.html.
|
|
16
20
|
* Note: it always uses the branch `main`.
|
|
@@ -20,16 +24,14 @@ import { SCOPE_PIPELINE } from './ConfigContext.js';
|
|
|
20
24
|
* @returns {Promise<HelixConfig|null>} the helix-config or {@code null} if optional and not found.
|
|
21
25
|
*/
|
|
22
26
|
async function fetchHelixConfig(ctx, rso) {
|
|
23
|
-
const codeBus = ctx.storage.codeBus();
|
|
24
27
|
const key = `${rso.org}/${rso.site}/main/helix-config.json`;
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
if (!data) {
|
|
28
|
+
const res = await ctx.loader.getObject(HELIX_CODE_BUS, key);
|
|
29
|
+
if (!res.body) {
|
|
28
30
|
return null;
|
|
29
31
|
}
|
|
30
|
-
const config =
|
|
32
|
+
const config = res.json();
|
|
31
33
|
// set contentbus id, if present in header
|
|
32
|
-
const cbid =
|
|
34
|
+
const cbid = res.headers.get('x-amz-meta-x-contentbus-id');
|
|
33
35
|
if (cbid && !config.content) {
|
|
34
36
|
// create the content section if not already present
|
|
35
37
|
config.content = {
|
|
@@ -57,13 +59,12 @@ async function fetchHelixConfig(ctx, rso) {
|
|
|
57
59
|
* @returns {Promise<ConfigAll|null>} the project configuration
|
|
58
60
|
*/
|
|
59
61
|
async function fetchConfigAll(ctx, contentBusId, partition) {
|
|
60
|
-
const contentBus = ctx.storage.contentBus();
|
|
61
62
|
const key = `${contentBusId}/${partition}/.helix/config-all.json`;
|
|
62
|
-
const
|
|
63
|
-
if (!
|
|
63
|
+
const res = await ctx.loader.getObject(HELIX_CONTENT_BUS, key);
|
|
64
|
+
if (!res.body) {
|
|
64
65
|
return null;
|
|
65
66
|
}
|
|
66
|
-
return
|
|
67
|
+
return res.json();
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
package/src/config-view.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
* governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
import { ModifiersConfig } from '@adobe/helix-shared-config';
|
|
12
|
+
import { ModifiersConfig } from '@adobe/helix-shared-config/modifiers';
|
|
13
13
|
import { computeSurrogateKey } from '@adobe/helix-shared-utils';
|
|
14
14
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
15
15
|
import { SCOPE_ADMIN, SCOPE_PIPELINE, SCOPE_DELIVERY } from './ConfigContext.js';
|
|
@@ -34,6 +34,12 @@ import { resolveLegacyConfig } from './config-legacy.js';
|
|
|
34
34
|
|
|
35
35
|
const METADATA_JSON = '/metadata.json';
|
|
36
36
|
|
|
37
|
+
const HELIX_CODE_BUS = 'helix-code-bus';
|
|
38
|
+
|
|
39
|
+
const HELIX_CONFIG_BUS = 'helix-config-bus';
|
|
40
|
+
|
|
41
|
+
const HELIX_CONTENT_BUS = 'helix-content-bus';
|
|
42
|
+
|
|
37
43
|
export function toArray(v) {
|
|
38
44
|
if (!v) {
|
|
39
45
|
return [];
|
|
@@ -77,7 +83,6 @@ export function getAccessConfig(access, partition) {
|
|
|
77
83
|
* @returns {Promise<{data: ModifierMap}|{}>} the metadata
|
|
78
84
|
*/
|
|
79
85
|
async function loadMetadata(ctx, config, partition) {
|
|
80
|
-
const contentBus = ctx.storage.contentBus();
|
|
81
86
|
const paths = config.metadata?.source ?? [];
|
|
82
87
|
if (!paths.length) {
|
|
83
88
|
paths.push(METADATA_JSON);
|
|
@@ -86,11 +91,11 @@ async function loadMetadata(ctx, config, partition) {
|
|
|
86
91
|
// generate the metadata-all.json first
|
|
87
92
|
const metadata = [];
|
|
88
93
|
for (const path of paths) {
|
|
89
|
-
const key =
|
|
94
|
+
const key = `${config.content.contentBusId}/${partition}${path}`;
|
|
90
95
|
// eslint-disable-next-line no-await-in-loop
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
const json =
|
|
96
|
+
const res = await ctx.loader.getObject(HELIX_CONTENT_BUS, key);
|
|
97
|
+
if (res.body) {
|
|
98
|
+
const json = res.json();
|
|
94
99
|
const data = json.data || json.default?.data;
|
|
95
100
|
if (data) {
|
|
96
101
|
metadata.push(...data);
|
|
@@ -109,12 +114,11 @@ async function loadMetadata(ctx, config, partition) {
|
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
async function loadHeadHtml(ctx, config, ref) {
|
|
112
|
-
const codeBus = ctx.storage.codeBus();
|
|
113
117
|
const key = `${config.code.owner}/${config.code.repo}/${ref}/head.html`;
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
118
|
+
const res = await ctx.loader.getObject(HELIX_CODE_BUS, key);
|
|
119
|
+
if (res.body) {
|
|
116
120
|
return {
|
|
117
|
-
html:
|
|
121
|
+
html: res.body,
|
|
118
122
|
};
|
|
119
123
|
}
|
|
120
124
|
return {};
|
|
@@ -143,17 +147,16 @@ function retainProperty(obj, prop) {
|
|
|
143
147
|
*/
|
|
144
148
|
async function resolveConfig(ctx, rso, scope) {
|
|
145
149
|
// try to load site config from config-bus
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
if (!buf) {
|
|
150
|
+
const key = `orgs/${rso.org}/sites/${rso.site}.json`;
|
|
151
|
+
const res = await ctx.loader.getObject(HELIX_CONFIG_BUS, key);
|
|
152
|
+
if (!res.body) {
|
|
150
153
|
if (scope !== SCOPE_ADMIN) {
|
|
151
154
|
return resolveLegacyConfig(ctx, rso, scope);
|
|
152
155
|
} else {
|
|
153
156
|
return null;
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
|
-
const config =
|
|
159
|
+
const config = res.json();
|
|
157
160
|
if (scope === SCOPE_PIPELINE) {
|
|
158
161
|
config.metadata = {
|
|
159
162
|
preview: await loadMetadata(ctx, config, 'preview'),
|
|
@@ -178,10 +181,14 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
178
181
|
} = opts;
|
|
179
182
|
const rso = { ref, site, org };
|
|
180
183
|
const config = await resolveConfig(ctx, rso, scope);
|
|
184
|
+
const surrogateHeaders = {
|
|
185
|
+
'x-surrogate-key': await getSurrogateKey(opts),
|
|
186
|
+
};
|
|
181
187
|
if (!config) {
|
|
182
188
|
return new PipelineResponse('', {
|
|
183
189
|
status: 404,
|
|
184
190
|
headers: {
|
|
191
|
+
...surrogateHeaders,
|
|
185
192
|
'x-error': 'config not found.',
|
|
186
193
|
},
|
|
187
194
|
});
|
|
@@ -200,15 +207,10 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
|
|
203
|
-
const headers = {
|
|
204
|
-
'content-type': 'application/json',
|
|
205
|
-
'x-surrogate-key': await getSurrogateKey(opts),
|
|
206
|
-
};
|
|
207
|
-
|
|
208
210
|
if (opts.scope === SCOPE_DELIVERY) {
|
|
209
211
|
return new PipelineResponse('', {
|
|
210
212
|
headers: {
|
|
211
|
-
|
|
213
|
+
...surrogateHeaders,
|
|
212
214
|
'x-hlx-contentbus-id': config.content.contentBusId,
|
|
213
215
|
'x-hlx-owner': config.code.owner,
|
|
214
216
|
'x-hlx-repo': config.code.repo,
|
|
@@ -234,11 +236,25 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
234
236
|
},
|
|
235
237
|
};
|
|
236
238
|
return new PipelineResponse(JSON.stringify(adminConfig, null, 2), {
|
|
237
|
-
headers
|
|
239
|
+
headers: {
|
|
240
|
+
'content-type': 'application/json',
|
|
241
|
+
...surrogateHeaders,
|
|
242
|
+
},
|
|
238
243
|
});
|
|
239
244
|
}
|
|
240
245
|
|
|
241
246
|
if (opts.scope === SCOPE_PIPELINE) {
|
|
247
|
+
// validate that ref exists in code-bus
|
|
248
|
+
if (!config.head?.html) {
|
|
249
|
+
return new PipelineResponse('', {
|
|
250
|
+
status: 404,
|
|
251
|
+
headers: {
|
|
252
|
+
'x-error': 'ref does not exit (no head.html)',
|
|
253
|
+
...surrogateHeaders,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
242
258
|
// remove all properties except `host`. pipeline doesn't need the others.
|
|
243
259
|
retainProperty(config.cdn, 'host');
|
|
244
260
|
const pipelineConfig = {
|
|
@@ -255,7 +271,10 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
255
271
|
folders: config.folders,
|
|
256
272
|
};
|
|
257
273
|
return new PipelineResponse(JSON.stringify(pipelineConfig, null, 2), {
|
|
258
|
-
headers
|
|
274
|
+
headers: {
|
|
275
|
+
'content-type': 'application/json',
|
|
276
|
+
...surrogateHeaders,
|
|
277
|
+
},
|
|
259
278
|
});
|
|
260
279
|
}
|
|
261
280
|
|
|
@@ -266,6 +285,9 @@ export async function getConfigResponse(ctx, opts) {
|
|
|
266
285
|
public: {},
|
|
267
286
|
};
|
|
268
287
|
return new PipelineResponse(JSON.stringify(publicConfig, null, 2), {
|
|
269
|
-
headers
|
|
288
|
+
headers: {
|
|
289
|
+
'content-type': 'application/json',
|
|
290
|
+
...surrogateHeaders,
|
|
291
|
+
},
|
|
270
292
|
});
|
|
271
293
|
}
|
package/src/domain-view.js
CHANGED
|
@@ -12,10 +12,16 @@
|
|
|
12
12
|
import { PipelineResponse } from './PipelineResponse.js';
|
|
13
13
|
|
|
14
14
|
export async function getDomainResponse(ctx, domain) {
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
const res = await ctx.loader.headObject('helix-config-bus', `domains/${domain}`);
|
|
16
|
+
const location = res.headers.get('x-amz-meta-location');
|
|
17
|
+
if (res.status === 200 && location) {
|
|
18
|
+
return new PipelineResponse('', {
|
|
19
|
+
status: 200,
|
|
20
|
+
headers: {
|
|
21
|
+
location,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
19
25
|
return new PipelineResponse('', {
|
|
20
26
|
status: 404,
|
|
21
27
|
headers: {
|
|
@@ -23,10 +29,4 @@ export async function getDomainResponse(ctx, domain) {
|
|
|
23
29
|
},
|
|
24
30
|
});
|
|
25
31
|
}
|
|
26
|
-
return new PipelineResponse('', {
|
|
27
|
-
status: 200,
|
|
28
|
-
headers: {
|
|
29
|
-
Location: data.Metadata.location,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
32
|
}
|
package/src/index.d.ts
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
import { PipelineResponse } from './PipelineResponse';
|
|
13
13
|
import { ConfigContext, ConfigScope } from "./ConfigContext";
|
|
14
14
|
|
|
15
|
+
export * from './PipelineResponse';
|
|
16
|
+
export * from './S3Loader';
|
|
17
|
+
|
|
15
18
|
export declare class ConfigRequestOptions {
|
|
16
19
|
scope: ConfigScope;
|
|
17
20
|
org: string;
|
package/src/HelixStorage.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2024 Adobe. All rights reserved.
|
|
3
|
-
* This file is licensed to you under the Apache License; Version 2.0 (the "License");
|
|
4
|
-
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
-
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
*
|
|
7
|
-
* Unless required by applicable law or agreed to in writing; software distributed under
|
|
8
|
-
* the License is distributed on an "AS IS" BASIS; WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
-
* OF ANY KIND; either express or implied. See the License for the specific language
|
|
10
|
-
* governing permissions and limitations under the License.
|
|
11
|
-
*/
|
|
12
|
-
export declare interface Bucket {
|
|
13
|
-
/**
|
|
14
|
-
* Return an object contents.
|
|
15
|
-
*
|
|
16
|
-
* @param {string} key object key
|
|
17
|
-
* @param {object} [meta] output object to receive metadata if specified
|
|
18
|
-
* @returns {Promise<Buffer>}object contents as a Buffer or null if no found.
|
|
19
|
-
* @throws an error if the object could not be loaded due to an unexpected error.
|
|
20
|
-
*/
|
|
21
|
-
get(key:string, meta?:object): Promise<Buffer|null>
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Performs a head request
|
|
25
|
-
* @param key
|
|
26
|
-
* @returns the head metadata or null
|
|
27
|
-
*/
|
|
28
|
-
head(key:string): Promise<object|null>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export declare interface HelixStorage {
|
|
32
|
-
/**
|
|
33
|
-
* Returns a bucket with the given identifier.
|
|
34
|
-
* @param {string} bucketId
|
|
35
|
-
* @returns {Bucket} the s3 object or null if not found
|
|
36
|
-
* @throws {Error} if the s3 object cannot be retrieved
|
|
37
|
-
*/
|
|
38
|
-
bucket(bucketId:string): Bucket;
|
|
39
|
-
|
|
40
|
-
contentBus(): Bucket;
|
|
41
|
-
|
|
42
|
-
codeBus(): Bucket;
|
|
43
|
-
|
|
44
|
-
configBus(): Bucket;
|
|
45
|
-
|
|
46
|
-
mediaBus(): Bucket;
|
|
47
|
-
}
|