@adobe/helix-config-storage 1.2.3 → 1.3.1

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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.3.1](https://github.com/adobe/helix-config-storage/compare/v1.3.0...v1.3.1) (2024-08-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Add site access entry in the config store ([#11](https://github.com/adobe/helix-config-storage/issues/11)) ([9fa8439](https://github.com/adobe/helix-config-storage/commit/9fa843923a89ca6a360e122c7ebb94dbac1c09ce))
7
+
8
+ # [1.3.0](https://github.com/adobe/helix-config-storage/compare/v1.2.3...v1.3.0) (2024-08-26)
9
+
10
+
11
+ ### Features
12
+
13
+ * include profile aggregate in purge call ([02a3580](https://github.com/adobe/helix-config-storage/commit/02a3580bbae7f4d2f646f8de4e92a9efc0bedd60))
14
+
1
15
  ## [1.2.3](https://github.com/adobe/helix-config-storage/compare/v1.2.2...v1.2.3) (2024-08-24)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-config-storage",
3
- "version": "1.2.3",
3
+ "version": "1.3.1",
4
4
  "description": "Helix Config Storage",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -38,6 +38,7 @@ const FRAGMENTS_COMMON = {
38
38
  },
39
39
  access: {
40
40
  '.': 'object',
41
+ site: 'object',
41
42
  admin: 'object',
42
43
  preview: 'object',
43
44
  live: 'object',
@@ -158,6 +159,31 @@ function redact(config, frag) {
158
159
  * General purpose config store.
159
160
  */
160
161
  export class ConfigStore {
162
+ /**
163
+ * @member {string} org the org id
164
+ */
165
+ org;
166
+
167
+ /**
168
+ * @member {string} type store type (org, sites, profiles, secrets, users)
169
+ */
170
+ type;
171
+
172
+ /**
173
+ * @member {string} name config name
174
+ */
175
+ name;
176
+
177
+ /**
178
+ * @member {string} key storage key
179
+ */
180
+ key;
181
+
182
+ /**
183
+ * @member {object} profiles the cached profiles config
184
+ */
185
+ #profiles = {};
186
+
161
187
  /**
162
188
  * @param {string} org the org id
163
189
  * @param {string} type store type (org, sites, profiles, secrets, users)
@@ -174,8 +200,54 @@ export class ConfigStore {
174
200
  this.type = type;
175
201
  this.name = name;
176
202
  this.key = type === 'org'
177
- ? `/orgs/${this.org}/${this.name || 'config'}.json`
178
- : `/orgs/${this.org}/${this.type}/${this.name}.json`;
203
+ ? `/orgs/${org}/${name || 'config'}.json`
204
+ : `/orgs/${org}/${type}/${name}.json`;
205
+ }
206
+
207
+ async #list(ctx) {
208
+ const storage = HelixStorage.fromContext(ctx).configBus();
209
+ const key = `orgs/${this.org}/${this.type}/`;
210
+ const list = await storage.list(key);
211
+ return {
212
+ [this.type]: list.map((entry) => {
213
+ const siteKey = entry.key;
214
+ if (siteKey.endsWith('.json')) {
215
+ const name = siteKey.split('/').pop();
216
+ return {
217
+ path: `/config/${this.org}/${this.type}/${name}`,
218
+ name: name.substring(0, name.length - 5),
219
+ };
220
+ }
221
+ return null;
222
+ }).filter((entry) => !!entry),
223
+ };
224
+ }
225
+
226
+ async #getProfile(ctx, data) {
227
+ const profileName = data.extends?.profile || 'default';
228
+ let profile = this.#profiles[profileName];
229
+ if (profile !== undefined) {
230
+ return profile;
231
+ }
232
+ const storage = HelixStorage.fromContext(ctx).configBus();
233
+ const profileJson = await storage.get(`/orgs/${this.org}/profiles/${profileName}.json`);
234
+ profile = profileJson ? JSON.parse(profileJson) : null;
235
+ this.#profiles[profileName] = profile;
236
+ return profile;
237
+ }
238
+
239
+ /**
240
+ * Returns the aggregated config. For site configs, this includes the profile config.
241
+ * @param {AdminContext} ctx
242
+ * @param {HelixSiteConfig|HelixProfileConfig} data the data of the config to be updated
243
+ * @returns {Promise<HelixSiteConfig|HelixProfileConfig>}
244
+ */
245
+ async getAggregatedConfig(ctx, data) {
246
+ if (this.type !== 'sites' || !data) {
247
+ return data;
248
+ }
249
+ const profile = await this.#getProfile(ctx, data);
250
+ return getMergedConfig(data, profile);
179
251
  }
180
252
 
181
253
  /**
@@ -185,16 +257,7 @@ export class ConfigStore {
185
257
  * @returns {Promise<boolean|undefined>}
186
258
  */
187
259
  async validate(ctx, data) {
188
- let config = data;
189
- if (this.type === 'sites') {
190
- // validate the config after merging with the profile
191
- const storage = HelixStorage.fromContext(ctx).configBus();
192
- const profileName = data.extends?.profile || 'default';
193
- const profileJson = await storage.get(`/orgs/${this.org}/profiles/${profileName}.json`);
194
- const profile = profileJson ? JSON.parse(profileJson) : null;
195
- config = getMergedConfig(config, profile);
196
- }
197
- return validateSchema(config, this.type);
260
+ return validateSchema(data, this.type);
198
261
  }
199
262
 
200
263
  async create(ctx, data, relPath = '') {
@@ -215,28 +278,10 @@ export class ConfigStore {
215
278
  updateContentSource(ctx, data.content);
216
279
  updateCodeSource(ctx, data.code);
217
280
  }
218
- await this.validate(ctx, data);
281
+ const config = await this.getAggregatedConfig(ctx, data);
282
+ await this.validate(ctx, config);
219
283
  await storage.put(this.key, JSON.stringify(data), 'application/json');
220
- await this.purge(ctx, null, data);
221
- }
222
-
223
- async #list(ctx) {
224
- const storage = HelixStorage.fromContext(ctx).configBus();
225
- const key = `orgs/${this.org}/${this.type}/`;
226
- const list = await storage.list(key);
227
- return {
228
- [this.type]: list.map((entry) => {
229
- const siteKey = entry.key;
230
- if (siteKey.endsWith('.json')) {
231
- const name = siteKey.split('/').pop();
232
- return {
233
- path: `/config/${this.org}/${this.type}/${name}`,
234
- name: name.substring(0, name.length - 5),
235
- };
236
- }
237
- return null;
238
- }).filter((entry) => entry),
239
- };
284
+ await this.purge(ctx, null, config);
240
285
  }
241
286
 
242
287
  async read(ctx, relPath = '') {
@@ -337,9 +382,17 @@ export class ConfigStore {
337
382
  updateContentSource(ctx, config.content);
338
383
  updateCodeSource(ctx, config.code);
339
384
  }
340
- await this.validate(ctx, config);
385
+ let oldConfig = buf ? JSON.parse(buf) : null;
386
+ let newConfig = config;
387
+ if (this.type === 'sites') {
388
+ // apply profile
389
+ oldConfig = await this.getAggregatedConfig(ctx, oldConfig);
390
+ newConfig = await this.getAggregatedConfig(ctx, newConfig);
391
+ }
392
+
393
+ await this.validate(ctx, newConfig);
341
394
  await storage.put(this.key, JSON.stringify(config), 'application/json');
342
- await this.purge(ctx, buf ? JSON.parse(buf) : null, config);
395
+ await this.purge(ctx, oldConfig, newConfig);
343
396
  return ret ?? redact(data, frag);
344
397
  }
345
398
 
@@ -350,16 +403,23 @@ export class ConfigStore {
350
403
  throw new StatusCodeError(404, 'config not found.');
351
404
  }
352
405
  const frag = getFragmentInfo(this.type, relPath);
406
+ let oldConfig = JSON.parse(buf);
407
+ if (this.type === 'sites') {
408
+ // apply profile
409
+ oldConfig = await this.getAggregatedConfig(ctx, oldConfig);
410
+ }
411
+
353
412
  if (frag) {
354
413
  const data = deepPut(JSON.parse(buf), relPath, null);
355
- await this.validate(ctx, data);
414
+ const newConfig = await this.getAggregatedConfig(ctx, data);
415
+ await this.validate(ctx, newConfig);
356
416
  await storage.put(this.key, JSON.stringify(data), 'application/json');
357
- await this.purge(ctx, JSON.parse(buf), data);
417
+ await this.purge(ctx, oldConfig, newConfig);
358
418
  return;
359
419
  }
360
420
 
361
421
  await storage.remove(this.key);
362
- await this.purge(ctx, JSON.parse(buf), null);
422
+ await this.purge(ctx, oldConfig, null);
363
423
  }
364
424
 
365
425
  // eslint-disable-next-line class-methods-use-this,no-unused-vars