@adobe/spacecat-shared-tier-client 1.0.0 → 1.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 +14 -0
- package/package.json +1 -1
- package/src/tier-client.js +38 -31
- package/test/tier-client.test.js +66 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-tier-client-v1.1.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tier-client-v1.0.1...@adobe/spacecat-shared-tier-client-v1.1.0) (2025-09-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* entitlement with out site and upgrade ([#971](https://github.com/adobe/spacecat-shared/issues/971)) ([1f042df](https://github.com/adobe/spacecat-shared/commit/1f042df62439383ff4a7cea6b0eb75649439e72a))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-tier-client-v1.0.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tier-client-v1.0.0...@adobe/spacecat-shared-tier-client-v1.0.1) (2025-09-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* fix in create org entity ([#965](https://github.com/adobe/spacecat-shared/issues/965)) ([406cbf3](https://github.com/adobe/spacecat-shared/commit/406cbf3cd214f3ec3b332ca48d8a743c123a2ddd))
|
|
14
|
+
|
|
1
15
|
# @adobe/spacecat-shared-tier-client-v1.0.0 (2025-09-16)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
package/src/tier-client.js
CHANGED
|
@@ -58,7 +58,8 @@ class TierClient {
|
|
|
58
58
|
if (!isNonEmptyObject(context)) {
|
|
59
59
|
throw new Error('Context is required');
|
|
60
60
|
}
|
|
61
|
-
const
|
|
61
|
+
const organizationId = await site.getOrganizationId();
|
|
62
|
+
const organization = await context.dataAccess.Organization.findById(organizationId);
|
|
62
63
|
return new TierClient(context, organization, site, productCode);
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -148,9 +149,10 @@ class TierClient {
|
|
|
148
149
|
|
|
149
150
|
/**
|
|
150
151
|
* Creates entitlement for organization and site enrollment for site.
|
|
151
|
-
*
|
|
152
|
+
* If entitlement exists with different tier, updates the tier.
|
|
152
153
|
* @param {string} tier - Entitlement tier.
|
|
153
|
-
* @returns {Promise<object>} Object with created
|
|
154
|
+
* @returns {Promise<object>} Object with created/updated
|
|
155
|
+
* entitlement and siteEnrollment (if site provided).
|
|
154
156
|
*/
|
|
155
157
|
async createEntitlement(tier) {
|
|
156
158
|
try {
|
|
@@ -158,42 +160,41 @@ class TierClient {
|
|
|
158
160
|
throw new Error(`Invalid tier: ${tier}. Valid tiers: ${Object.values(EntitlementModel.TIERS).join(', ')}`);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
if (!this.site) {
|
|
162
|
-
throw new Error('Site required for creating entitlements');
|
|
163
|
-
}
|
|
164
|
-
|
|
165
163
|
const orgId = this.organization.getId();
|
|
166
|
-
|
|
167
|
-
this.log.info(`Creating entitlement for org ${orgId}, site ${siteId}, product ${this.productCode}, tier ${tier}`);
|
|
164
|
+
this.log.info(`Creating/updating entitlement for org ${orgId}, product ${this.productCode}, tier ${tier}`);
|
|
168
165
|
|
|
169
166
|
// Check what already exists
|
|
170
167
|
const existing = await this.checkValidEntitlement();
|
|
171
168
|
|
|
172
|
-
// If
|
|
173
|
-
if (existing.entitlement
|
|
174
|
-
|
|
175
|
-
return existing;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// If only entitlement exists, we need to create site enrollment
|
|
179
|
-
if (existing.entitlement && !existing.siteEnrollment) {
|
|
180
|
-
this.log.info(`Entitlement exists but site enrollment missing for org ${orgId}, site ${siteId} and product ${this.productCode}`);
|
|
169
|
+
// If entitlement exists, handle tier update and return
|
|
170
|
+
if (existing.entitlement) {
|
|
171
|
+
const currentTier = existing.entitlement.getTier();
|
|
181
172
|
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
173
|
+
// If tier doesn't match, update it
|
|
174
|
+
if (currentTier !== tier) {
|
|
175
|
+
this.log.info(`Updating entitlement tier from ${currentTier} to ${tier} for org ${orgId}`);
|
|
176
|
+
existing.entitlement.setTier(tier);
|
|
177
|
+
await existing.entitlement.save();
|
|
178
|
+
}
|
|
187
179
|
|
|
188
|
-
|
|
180
|
+
// If site provided but no site enrollment, create it
|
|
181
|
+
if (this.site && !existing.siteEnrollment) {
|
|
182
|
+
const siteId = this.site.getId();
|
|
183
|
+
const siteEnrollment = await this.SiteEnrollment.create({
|
|
184
|
+
siteId,
|
|
185
|
+
entitlementId: existing.entitlement.getId(),
|
|
186
|
+
});
|
|
187
|
+
this.log.info(`Created site enrollment: ${siteEnrollment.getId()}`);
|
|
188
|
+
return {
|
|
189
|
+
entitlement: existing.entitlement,
|
|
190
|
+
siteEnrollment,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
189
193
|
|
|
190
|
-
return
|
|
191
|
-
entitlement: existing.entitlement,
|
|
192
|
-
siteEnrollment,
|
|
193
|
-
};
|
|
194
|
+
return existing;
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
//
|
|
197
|
+
// No existing entitlement, create new one
|
|
197
198
|
const entitlement = await this.Entitlement.create({
|
|
198
199
|
organizationId: orgId,
|
|
199
200
|
productCode: this.productCode,
|
|
@@ -204,9 +205,15 @@ class TierClient {
|
|
|
204
205
|
},
|
|
205
206
|
});
|
|
206
207
|
|
|
207
|
-
this.log.info(`Created entitlement: ${entitlement.getId()}`);
|
|
208
|
+
this.log.info(`Created new entitlement: ${entitlement.getId()}`);
|
|
209
|
+
|
|
210
|
+
// If no site provided, return entitlement only
|
|
211
|
+
if (!this.site) {
|
|
212
|
+
return { entitlement };
|
|
213
|
+
}
|
|
208
214
|
|
|
209
215
|
// Create site enrollment
|
|
216
|
+
const siteId = this.site.getId();
|
|
210
217
|
const siteEnrollment = await this.SiteEnrollment.create({
|
|
211
218
|
siteId,
|
|
212
219
|
entitlementId: entitlement.getId(),
|
|
@@ -219,7 +226,7 @@ class TierClient {
|
|
|
219
226
|
siteEnrollment,
|
|
220
227
|
};
|
|
221
228
|
} catch (error) {
|
|
222
|
-
this.log.error(`Error creating entitlement
|
|
229
|
+
this.log.error(`Error creating/updating entitlement: ${error.message}`);
|
|
223
230
|
throw error;
|
|
224
231
|
}
|
|
225
232
|
}
|
package/test/tier-client.test.js
CHANGED
|
@@ -128,7 +128,7 @@ describe('TierClient', () => {
|
|
|
128
128
|
const testSiteWithOrgRef = Object.create(Site.prototype);
|
|
129
129
|
Object.assign(testSiteWithOrgRef, {
|
|
130
130
|
getId: () => siteId,
|
|
131
|
-
|
|
131
|
+
getOrganizationId: () => orgId,
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
describe('createForOrg', () => {
|
|
@@ -165,19 +165,23 @@ describe('TierClient', () => {
|
|
|
165
165
|
|
|
166
166
|
describe('createForSite', () => {
|
|
167
167
|
it('should create TierClient for site with getOrganizationId', async () => {
|
|
168
|
+
mockDataAccess.Organization.findById.resolves(testOrganization);
|
|
168
169
|
const client = await TierClient.createForSite(mockContext, testSite, productCode);
|
|
169
170
|
|
|
170
171
|
expect(client).to.be.an('object');
|
|
171
172
|
expect(client.checkValidEntitlement).to.be.a('function');
|
|
172
173
|
expect(client.createEntitlement).to.be.a('function');
|
|
174
|
+
expect(mockDataAccess.Organization.findById).to.have.been.calledWith(orgId);
|
|
173
175
|
});
|
|
174
176
|
|
|
175
|
-
it('should create TierClient for site with
|
|
177
|
+
it('should create TierClient for site with getOrganizationId (alternative)', async () => {
|
|
178
|
+
mockDataAccess.Organization.findById.resolves(testOrganization);
|
|
176
179
|
const client = await TierClient.createForSite(mockContext, testSiteWithOrgRef, productCode);
|
|
177
180
|
|
|
178
181
|
expect(client).to.be.an('object');
|
|
179
182
|
expect(client.checkValidEntitlement).to.be.a('function');
|
|
180
183
|
expect(client.createEntitlement).to.be.a('function');
|
|
184
|
+
expect(mockDataAccess.Organization.findById).to.have.been.calledWith(orgId);
|
|
181
185
|
});
|
|
182
186
|
|
|
183
187
|
it('should throw error when site is not provided', async () => {
|
|
@@ -199,7 +203,7 @@ describe('TierClient', () => {
|
|
|
199
203
|
|
|
200
204
|
it('should throw error when dataAccess is missing', async () => {
|
|
201
205
|
const invalidContext = { log: {} };
|
|
202
|
-
await expect(TierClient.createForSite(invalidContext, testSite, productCode)).to.be.rejectedWith('Cannot
|
|
206
|
+
await expect(TierClient.createForSite(invalidContext, testSite, productCode)).to.be.rejectedWith('Cannot read properties of undefined');
|
|
203
207
|
});
|
|
204
208
|
});
|
|
205
209
|
});
|
|
@@ -347,7 +351,33 @@ describe('TierClient', () => {
|
|
|
347
351
|
await expect(tierClient.createEntitlement('INVALID_TIER')).to.be.rejectedWith('Invalid tier: INVALID_TIER');
|
|
348
352
|
});
|
|
349
353
|
|
|
350
|
-
it('should
|
|
354
|
+
it('should work without site for createEntitlement (organization only)', async () => {
|
|
355
|
+
// Create a TierClient without site
|
|
356
|
+
const tierClientWithoutSite = new TierClient(
|
|
357
|
+
mockContext,
|
|
358
|
+
organizationInstance,
|
|
359
|
+
null,
|
|
360
|
+
productCode,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(null);
|
|
364
|
+
mockDataAccess.Entitlement.create.resolves(mockEntitlement);
|
|
365
|
+
|
|
366
|
+
const result = await tierClientWithoutSite.createEntitlement('FREE_TRIAL');
|
|
367
|
+
|
|
368
|
+
expect(result).to.deep.equal({
|
|
369
|
+
entitlement: mockEntitlement,
|
|
370
|
+
});
|
|
371
|
+
expect(mockDataAccess.Entitlement.create).to.have.been.calledWith({
|
|
372
|
+
organizationId: orgId,
|
|
373
|
+
productCode,
|
|
374
|
+
tier: 'FREE_TRIAL',
|
|
375
|
+
quotas: { llmo_trial_prompts: 200, llmo_trial_prompts_consumed: 0 },
|
|
376
|
+
});
|
|
377
|
+
expect(mockDataAccess.SiteEnrollment.create).to.not.have.been.called;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should return existing entitlement when site is not provided and entitlement exists', async () => {
|
|
351
381
|
// Create a TierClient without site
|
|
352
382
|
const tierClientWithoutSite = new TierClient(
|
|
353
383
|
mockContext,
|
|
@@ -356,7 +386,38 @@ describe('TierClient', () => {
|
|
|
356
386
|
productCode,
|
|
357
387
|
);
|
|
358
388
|
|
|
359
|
-
|
|
389
|
+
mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
|
|
390
|
+
|
|
391
|
+
const result = await tierClientWithoutSite.createEntitlement('FREE_TRIAL');
|
|
392
|
+
|
|
393
|
+
expect(result).to.deep.equal({
|
|
394
|
+
entitlement: mockEntitlement,
|
|
395
|
+
});
|
|
396
|
+
expect(mockDataAccess.Entitlement.create).to.not.have.been.called;
|
|
397
|
+
expect(mockDataAccess.SiteEnrollment.create).to.not.have.been.called;
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('should update tier when entitlement exists with different tier', async () => {
|
|
401
|
+
const mockEntitlementWithDifferentTier = {
|
|
402
|
+
...mockEntitlement,
|
|
403
|
+
getTier: () => 'PAID',
|
|
404
|
+
setTier: sandbox.stub().returnsThis(),
|
|
405
|
+
save: sandbox.stub().resolves(),
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
mockDataAccess.Entitlement
|
|
409
|
+
.findByOrganizationIdAndProductCode.resolves(mockEntitlementWithDifferentTier);
|
|
410
|
+
mockDataAccess.SiteEnrollment.allBySiteId.resolves([mockSiteEnrollment]);
|
|
411
|
+
|
|
412
|
+
const result = await tierClient.createEntitlement('FREE_TRIAL');
|
|
413
|
+
|
|
414
|
+
expect(mockEntitlementWithDifferentTier.setTier).to.have.been.calledWith('FREE_TRIAL');
|
|
415
|
+
expect(mockEntitlementWithDifferentTier.save).to.have.been.called;
|
|
416
|
+
expect(result).to.deep.equal({
|
|
417
|
+
entitlement: mockEntitlementWithDifferentTier,
|
|
418
|
+
siteEnrollment: mockSiteEnrollment,
|
|
419
|
+
});
|
|
420
|
+
expect(mockDataAccess.Entitlement.create).to.not.have.been.called;
|
|
360
421
|
});
|
|
361
422
|
|
|
362
423
|
it('should throw error when organization not found', async () => {
|