@adobe/spacecat-shared-data-access 4.21.0 → 4.22.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 +12 -0
- package/package.json +1 -1
- package/src/models/brand/brand.collection.js +3 -2
- package/src/models/brand/brand.model.js +9 -19
- package/src/models/brand/brand.schema.js +9 -29
- package/src/models/brand/index.d.ts +0 -11
- package/src/models/trial-user/index.d.ts +20 -0
- package/src/models/trial-user/trial-user.schema.js +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-data-access-v4.22.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v4.21.1...@adobe/spacecat-shared-data-access-v4.22.0) (2026-08-14)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* schema changes for trial users entity to allow searching by external user id ([#1871](https://github.com/adobe/spacecat-shared/issues/1871)) ([3ecf546](https://github.com/adobe/spacecat-shared/commit/3ecf5466b41dcd32cf5dd591f8762fefd714af1f))
|
|
6
|
+
|
|
7
|
+
## [@adobe/spacecat-shared-data-access-v4.21.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v4.21.0...@adobe/spacecat-shared-data-access-v4.21.1) (2026-08-14)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **data-access:** publish brand semrushWorkspaceId mirror removal (SITES-49202) ([#1880](https://github.com/adobe/spacecat-shared/issues/1880)) ([80a296a](https://github.com/adobe/spacecat-shared/commit/80a296af1b4f49ffa66aba80d677a5e6b42f3856)), closes [#1867](https://github.com/adobe/spacecat-shared/issues/1867) [#1867](https://github.com/adobe/spacecat-shared/issues/1867) [#1867](https://github.com/adobe/spacecat-shared/issues/1867)
|
|
12
|
+
|
|
1
13
|
## [@adobe/spacecat-shared-data-access-v4.21.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-data-access-v4.20.0...@adobe/spacecat-shared-data-access-v4.21.0) (2026-08-13)
|
|
2
14
|
|
|
3
15
|
### Features
|
package/package.json
CHANGED
|
@@ -15,8 +15,9 @@ import BaseCollection from '../base/base.collection.js';
|
|
|
15
15
|
/**
|
|
16
16
|
* BrandCollection - collection of Brand rows. `findById` (the brand UUID) is
|
|
17
17
|
* the primary access path for serenity dual-mode resolution;
|
|
18
|
-
* `
|
|
19
|
-
* pointer when a sub-workspace is found to have been deleted
|
|
18
|
+
* `findBySemrushSubWorkspaceId` (from the addAllIndex on the schema) is used to
|
|
19
|
+
* repair a pointer when a sub-workspace is found to have been deleted
|
|
20
|
+
* out-of-band.
|
|
20
21
|
*
|
|
21
22
|
* @class BrandCollection
|
|
22
23
|
* @extends BaseCollection
|
|
@@ -16,17 +16,24 @@ import BaseModel from '../base/base.model.js';
|
|
|
16
16
|
* Brand - an Adobe brand, stored in the `brands` table in mysticat-data-service
|
|
17
17
|
* and served over PostgREST. Intentionally minimal: it surfaces only the fields
|
|
18
18
|
* the serenity sub-workspace provisioning flows read/write
|
|
19
|
-
* (`
|
|
19
|
+
* (`semrushSubWorkspaceId`, `status`, `name`). Brands are created and fully
|
|
20
20
|
* managed elsewhere (Brandalf sync, onboarding); this entity is a read +
|
|
21
21
|
* targeted-patch surface, not a create surface.
|
|
22
22
|
*
|
|
23
|
-
* `
|
|
23
|
+
* `semrushSubWorkspaceId` is the dual-mode switch: NULL = the brand is not
|
|
24
24
|
* connected to a Semrush sub-workspace (resolves against the org parent
|
|
25
25
|
* workspace — "flat" mode); set = the brand has its own Semrush sub-workspace.
|
|
26
26
|
* Deactivation empties the sub-workspace and clears this pointer (the
|
|
27
27
|
* sub-workspace itself is never deleted). See serenity-docs
|
|
28
28
|
* brand-semrush-provisioning-v2-phase1-sync.md §6.
|
|
29
29
|
*
|
|
30
|
+
* NOTE: there is no brand-level `semrushWorkspaceId` accessor. The deprecated
|
|
31
|
+
* read-only mirror (attribute, index, `findBySemrushWorkspaceId`,
|
|
32
|
+
* `allBySemrushWorkspaceId`, `setSemrushWorkspaceId`) was removed in SITES-49202;
|
|
33
|
+
* `semrushSubWorkspaceId` above is the write-of-record. The identically-named
|
|
34
|
+
* `Organization.semrushWorkspaceId` is a DISTINCT field and stays — do not
|
|
35
|
+
* reintroduce a brand mirror by symbol-name sweep.
|
|
36
|
+
*
|
|
30
37
|
* @class Brand
|
|
31
38
|
* @extends BaseModel
|
|
32
39
|
*/
|
|
@@ -39,23 +46,6 @@ class Brand extends BaseModel {
|
|
|
39
46
|
* `pending`; customer offboard writes `deleted`.
|
|
40
47
|
*/
|
|
41
48
|
static STATUSES = Object.freeze(['pending', 'active', 'deleted', 'ignored']);
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Deprecated BC-compat setter. `semrushWorkspaceId` is `readOnly: true` in
|
|
45
|
-
* the schema (mirrored by the mysticat-data-service sync trigger), so no
|
|
46
|
-
* setter is auto-generated for it — this manual method exists purely so an
|
|
47
|
-
* existing external caller of `setSemrushWorkspaceId` does not get a
|
|
48
|
-
* semver-breaking runtime error on upgrade. Delegates to the real
|
|
49
|
-
* write-of-record attribute. Remove once every direct caller has migrated
|
|
50
|
-
* to `setSemrushSubWorkspaceId` (see brand.schema.js).
|
|
51
|
-
*
|
|
52
|
-
* @deprecated Use setSemrushSubWorkspaceId instead.
|
|
53
|
-
* @param {string|null} value
|
|
54
|
-
* @returns {Brand}
|
|
55
|
-
*/
|
|
56
|
-
setSemrushWorkspaceId(value) {
|
|
57
|
-
return this.setSemrushSubWorkspaceId(value);
|
|
58
|
-
}
|
|
59
49
|
}
|
|
60
50
|
|
|
61
51
|
export default Brand;
|
|
@@ -28,37 +28,23 @@ const schema = new SchemaBuilder(Brand, BrandCollection)
|
|
|
28
28
|
})
|
|
29
29
|
// reference_status enum on the brands table. Not `required`: this entity
|
|
30
30
|
// never creates a brand, and a targeted PATCH (e.g. setting only
|
|
31
|
-
//
|
|
31
|
+
// semrushSubWorkspaceId) must not be forced to also send status. The validator
|
|
32
32
|
// still rejects an out-of-enum value when status IS written
|
|
33
33
|
// (activate → 'active', deactivate → 'pending').
|
|
34
34
|
.addAttribute('status', {
|
|
35
35
|
type: Brand.STATUSES,
|
|
36
36
|
validate: (value) => value == null || Brand.STATUSES.includes(value),
|
|
37
37
|
})
|
|
38
|
-
// DEPRECATED (serenity-docs brand-semrush-mapping-maintenance.md §10
|
|
39
|
-
// rename, write-of-record cutover): read-only mirror of
|
|
40
|
-
// semrushSubWorkspaceId below, maintained entirely by the
|
|
41
|
-
// mysticat-data-service brands_sync_semrush_workspace_id trigger
|
|
42
|
-
// (migration 20260702094229). No schema-generated setter — app code must
|
|
43
|
-
// write semrushSubWorkspaceId instead. brand.model.js still defines a
|
|
44
|
-
// manual, deprecated setSemrushWorkspaceId() that delegates to
|
|
45
|
-
// setSemrushSubWorkspaceId(), so an existing external caller of the old
|
|
46
|
-
// setter is not broken (a bare readOnly flip here would be a semver-breaking
|
|
47
|
-
// removal for any @adobe/spacecat-shared-data-access consumer). Will be
|
|
48
|
-
// retired (attribute, column, and trigger) once every direct external
|
|
49
|
-
// reader has migrated.
|
|
50
|
-
.addAttribute('semrushWorkspaceId', {
|
|
51
|
-
type: 'string',
|
|
52
|
-
readOnly: true,
|
|
53
|
-
})
|
|
54
38
|
// Brand → Semrush sub-workspace. Nullable (NULL = no sub-workspace
|
|
55
|
-
// connected). Same minimum guard
|
|
56
|
-
//
|
|
39
|
+
// connected). Same minimum guard the distinct `Organization` entity applies
|
|
40
|
+
// to its own `semrushWorkspaceId` field (the brand has no such field — its
|
|
41
|
+
// deprecated mirror was removed in SITES-49202): the shared `hasText` rejects
|
|
42
|
+
// the empty string (and non-strings) while letting
|
|
57
43
|
// null/undefined short-circuit. Note hasText does NOT trim, so a
|
|
58
44
|
// whitespace-only value would pass — acceptable here because this column is
|
|
59
45
|
// only ever written by the activate flow with a real Semrush workspace UUID,
|
|
60
|
-
// never user input. This is
|
|
61
|
-
//
|
|
46
|
+
// never user input. This is the write-of-record for the brand → Semrush
|
|
47
|
+
// sub-workspace pointer.
|
|
62
48
|
.addAttribute('semrushSubWorkspaceId', {
|
|
63
49
|
type: 'string',
|
|
64
50
|
validate: (value) => value == null || hasText(value),
|
|
@@ -72,15 +58,9 @@ const schema = new SchemaBuilder(Brand, BrandCollection)
|
|
|
72
58
|
type: 'any',
|
|
73
59
|
validate: (value) => value == null || (typeof value === 'object' && !Array.isArray(value)),
|
|
74
60
|
})
|
|
75
|
-
// Uniqueness
|
|
76
|
-
// deprecated brands.semrush_workspace_id (mysticat-data-service migration
|
|
77
|
-
// 20260615102123), so findBySemrushWorkspaceId returns at most one row.
|
|
78
|
-
// Kept for BC lookups against the mirrored column; new code should prefer
|
|
79
|
-
// findBySemrushSubWorkspaceId below.
|
|
80
|
-
.addAllIndex(['semrushWorkspaceId'])
|
|
81
|
-
// Same uniqueness guarantee on the write-of-record column
|
|
61
|
+
// Uniqueness guarantee on the write-of-record column
|
|
82
62
|
// (brands.semrush_sub_workspace_id, mysticat-data-service migration
|
|
83
|
-
// 20260702091920).
|
|
63
|
+
// 20260702091920), so findBySemrushSubWorkspaceId returns at most one row.
|
|
84
64
|
.addAllIndex(['semrushSubWorkspaceId']);
|
|
85
65
|
|
|
86
66
|
export default schema.build();
|
|
@@ -17,24 +17,13 @@ import type {
|
|
|
17
17
|
export interface Brand extends BaseModel {
|
|
18
18
|
getName(): string;
|
|
19
19
|
getStatus(): string;
|
|
20
|
-
// Deprecated BC mirror (brands.semrush_workspace_id), maintained by the
|
|
21
|
-
// mysticat-data-service sync trigger. No schema-generated setter; the
|
|
22
|
-
// deprecated setSemrushWorkspaceId below is a manual delegate defined in
|
|
23
|
-
// brand.model.js, kept only for backward compatibility. Use
|
|
24
|
-
// getSemrushSubWorkspaceId/setSemrushSubWorkspaceId instead. See
|
|
25
|
-
// brand.schema.js.
|
|
26
|
-
getSemrushWorkspaceId(): string | null;
|
|
27
20
|
getSemrushSubWorkspaceId(): string | null;
|
|
28
21
|
setName(value: string): Brand;
|
|
29
22
|
setStatus(value: string): Brand;
|
|
30
23
|
setSemrushSubWorkspaceId(value: string | null): Brand;
|
|
31
|
-
/** @deprecated Use setSemrushSubWorkspaceId instead. */
|
|
32
|
-
setSemrushWorkspaceId(value: string | null): Brand;
|
|
33
24
|
}
|
|
34
25
|
|
|
35
26
|
export interface BrandCollection extends BaseCollection<Brand> {
|
|
36
|
-
allBySemrushWorkspaceId(semrushWorkspaceId: string): Promise<Brand[]>;
|
|
37
|
-
findBySemrushWorkspaceId(semrushWorkspaceId: string): Promise<Brand | null>;
|
|
38
27
|
allBySemrushSubWorkspaceId(semrushSubWorkspaceId: string): Promise<Brand[]>;
|
|
39
28
|
findBySemrushSubWorkspaceId(semrushSubWorkspaceId: string): Promise<Brand | null>;
|
|
40
29
|
}
|
|
@@ -43,9 +43,29 @@ export interface TrialUserCollection extends BaseCollection<TrialUser> {
|
|
|
43
43
|
allByProviderAndExternalUserId(provider: ProviderType, externalId: string): Promise<TrialUser[]>;
|
|
44
44
|
allByOrganizationId(organizationId: string): Promise<TrialUser[]>;
|
|
45
45
|
allByEmailId(emailId: string): Promise<TrialUser[]>;
|
|
46
|
+
allByExternalUserId(externalUserId: string): Promise<TrialUser[]>;
|
|
47
|
+
allByExternalUserIdAndUpdatedAt(
|
|
48
|
+
externalUserId: string,
|
|
49
|
+
updatedAt: string
|
|
50
|
+
): Promise<TrialUser[]>;
|
|
46
51
|
findByProvider(provider: ProviderType): Promise<TrialUser | null>;
|
|
47
52
|
findByProviderAndExternalUserId(provider: ProviderType, externalId: string):
|
|
48
53
|
Promise<TrialUser | null>;
|
|
49
54
|
findByOrganizationId(organizationId: string): Promise<TrialUser | null>;
|
|
50
55
|
findByEmailId(emailId: string): Promise<TrialUser | null>;
|
|
56
|
+
/**
|
|
57
|
+
* externalUserId is optional and not enforced unique (no DB unique constraint), so this
|
|
58
|
+
* returns one arbitrary match when multiple trial users share the same externalUserId.
|
|
59
|
+
* Use allByExternalUserId if you need every matching record.
|
|
60
|
+
*/
|
|
61
|
+
findByExternalUserId(externalUserId: string): Promise<TrialUser | null>;
|
|
62
|
+
/**
|
|
63
|
+
* externalUserId is optional and not enforced unique (no DB unique constraint), so this
|
|
64
|
+
* returns one arbitrary match when multiple trial users share the same externalUserId.
|
|
65
|
+
* Use allByExternalUserIdAndUpdatedAt if you need every matching record.
|
|
66
|
+
*/
|
|
67
|
+
findByExternalUserIdAndUpdatedAt(
|
|
68
|
+
externalUserId: string,
|
|
69
|
+
updatedAt: string
|
|
70
|
+
): Promise<TrialUser | null>;
|
|
51
71
|
}
|
|
@@ -53,6 +53,11 @@ const schema = new SchemaBuilder(TrialUser, TrialUserCollection)
|
|
|
53
53
|
{ composite: ['organizationId'] },
|
|
54
54
|
{ composite: ['updatedAt'] },
|
|
55
55
|
)
|
|
56
|
+
// externalUserId is not enforced unique (no DB unique constraint) and is optional, so
|
|
57
|
+
// findByExternalUserId only returns one arbitrary match. Prefer allByExternalUserId
|
|
58
|
+
// unless the caller can guarantee uniqueness for their use case.
|
|
59
|
+
// Note: this schema is now at 4 of the 5-index cap enforced by SchemaBuilder
|
|
60
|
+
// (#buildIndexes) - budget the next lookup index accordingly.
|
|
56
61
|
.addIndex(
|
|
57
62
|
{ composite: ['externalUserId'] },
|
|
58
63
|
{ composite: ['updatedAt'] },
|