@adobe/spacecat-shared-tier-client 1.3.10 → 1.3.11

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,10 @@
1
+ # [@adobe/spacecat-shared-tier-client-v1.3.11](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tier-client-v1.3.10...@adobe/spacecat-shared-tier-client-v1.3.11) (2026-02-06)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * adds additional check for dangling enrollments ([#1296](https://github.com/adobe/spacecat-shared/issues/1296)) ([e5a0de3](https://github.com/adobe/spacecat-shared/commit/e5a0de32112dbf81e3da37d7b90a747d993497da))
7
+
1
8
  # [@adobe/spacecat-shared-tier-client-v1.3.10](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-tier-client-v1.3.9...@adobe/spacecat-shared-tier-client-v1.3.10) (2025-12-05)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-tier-client",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Shared modules of the Spacecat Services - Tier Client",
5
5
  "type": "module",
6
6
  "engines": {
@@ -220,6 +220,7 @@ class TierClient {
220
220
  * Gets all enrollments based on context, filtered by productCode.
221
221
  * - If site is provided: returns site enrollment for the entitlement matching productCode
222
222
  * - If org-only: returns all site enrollments for the entitlement matching productCode
223
+ * - Filters out enrollments where the site's orgId doesn't match the entitlement's orgId
223
224
  * @returns {Promise<object>} Object with entitlement and enrollments array.
224
225
  */
225
226
  async getAllEnrollment() {
@@ -234,16 +235,41 @@ class TierClient {
234
235
 
235
236
  const allEnrollments = await this.SiteEnrollment.allByEntitlementId(entitlement.getId());
236
237
 
238
+ if (allEnrollments.length === 0) {
239
+ return { entitlement, enrollments: [] };
240
+ }
241
+
242
+ // Fetch all sites using batchGetByKeys
243
+ const siteKeys = allEnrollments.map((enrollment) => ({ siteId: enrollment.getSiteId() }));
244
+ const sitesResult = await this.Site.batchGetByKeys(siteKeys);
245
+ const sitesMap = new Map(sitesResult.data.map((site) => [site.getId(), site]));
246
+
247
+ // Filter enrollments where site's orgId matches the entitlement's orgId
248
+ const validEnrollments = [];
249
+
250
+ for (const enrollment of allEnrollments) {
251
+ const site = sitesMap.get(enrollment.getSiteId());
252
+ if (!site) {
253
+ // Site not found, log warning and skip
254
+ this.log.warn(`Site not found for enrollment ${enrollment.getId()} with siteId ${enrollment.getSiteId()}`);
255
+ } else {
256
+ const siteOrgId = site.getOrganizationId();
257
+ if (siteOrgId === orgId) {
258
+ validEnrollments.push(enrollment);
259
+ }
260
+ }
261
+ }
262
+
237
263
  if (this.site) {
238
264
  // Return site enrollments matching the entitlement and site
239
265
  const siteId = this.site.getId();
240
- const matchingEnrollments = allEnrollments.filter(
266
+ const matchingEnrollments = validEnrollments.filter(
241
267
  (se) => se.getSiteId() === siteId,
242
268
  );
243
269
  return { entitlement, enrollments: matchingEnrollments };
244
270
  } else {
245
- // Return all enrollments for the entitlement
246
- return { entitlement, enrollments: allEnrollments };
271
+ // Return all valid enrollments for the entitlement
272
+ return { entitlement, enrollments: validEnrollments };
247
273
  }
248
274
  } catch (error) {
249
275
  this.log.error(`Error getting all enrollments: ${error.message}`);
@@ -80,6 +80,7 @@ describe('TierClient', () => {
80
80
  },
81
81
  Site: {
82
82
  findById: sandbox.stub(),
83
+ batchGetByKeys: sandbox.stub(),
83
84
  },
84
85
  };
85
86
 
@@ -726,11 +727,25 @@ describe('TierClient', () => {
726
727
  getEntitlementId: () => 'entitlement-123',
727
728
  };
728
729
 
730
+ const mockSiteForEnrollment1 = {
731
+ getId: () => siteId,
732
+ getOrganizationId: () => orgId,
733
+ };
734
+
735
+ const mockSiteForEnrollment2 = {
736
+ getId: () => '789-site-id',
737
+ getOrganizationId: () => orgId,
738
+ };
739
+
729
740
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
730
741
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([
731
742
  mockSiteEnrollment,
732
743
  mockEnrollment2,
733
744
  ]);
745
+ mockDataAccess.Site.batchGetByKeys.resolves({
746
+ data: [mockSiteForEnrollment1, mockSiteForEnrollment2],
747
+ unprocessed: [],
748
+ });
734
749
 
735
750
  const result = await tierClientWithoutSite.getAllEnrollment();
736
751
 
@@ -742,6 +757,10 @@ describe('TierClient', () => {
742
757
  .to.have.been.calledWith(orgId, productCode);
743
758
  expect(mockDataAccess.SiteEnrollment.allByEntitlementId)
744
759
  .to.have.been.calledWith('entitlement-123');
760
+ expect(mockDataAccess.Site.batchGetByKeys).to.have.been.calledWith([
761
+ { siteId },
762
+ { siteId: '789-site-id' },
763
+ ]);
745
764
  });
746
765
 
747
766
  it('should return filtered enrollments when site is provided', async () => {
@@ -751,11 +770,25 @@ describe('TierClient', () => {
751
770
  getEntitlementId: () => 'entitlement-123',
752
771
  };
753
772
 
773
+ const mockSiteForEnrollment1 = {
774
+ getId: () => siteId,
775
+ getOrganizationId: () => orgId,
776
+ };
777
+
778
+ const mockSiteForEnrollment2 = {
779
+ getId: () => 'other-site-id',
780
+ getOrganizationId: () => orgId,
781
+ };
782
+
754
783
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
755
784
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([
756
785
  mockSiteEnrollment,
757
786
  mockEnrollment2,
758
787
  ]);
788
+ mockDataAccess.Site.batchGetByKeys.resolves({
789
+ data: [mockSiteForEnrollment1, mockSiteForEnrollment2],
790
+ unprocessed: [],
791
+ });
759
792
 
760
793
  const result = await tierClient.getAllEnrollment();
761
794
 
@@ -811,18 +844,96 @@ describe('TierClient', () => {
811
844
  getEntitlementId: () => 'entitlement-123',
812
845
  };
813
846
 
847
+ const mockSiteForEnrollment1 = {
848
+ getId: () => siteId,
849
+ getOrganizationId: () => orgId,
850
+ };
851
+
852
+ const mockSiteForEnrollment2 = {
853
+ getId: () => 'different-site-id',
854
+ getOrganizationId: () => orgId,
855
+ };
856
+
814
857
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
815
858
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([
816
859
  mockSiteEnrollment,
817
860
  mockEnrollment2,
818
861
  mockEnrollment3,
819
862
  ]);
863
+ mockDataAccess.Site.batchGetByKeys.resolves({
864
+ data: [mockSiteForEnrollment1, mockSiteForEnrollment2, mockSiteForEnrollment1],
865
+ unprocessed: [],
866
+ });
820
867
 
821
868
  const result = await tierClient.getAllEnrollment();
822
869
 
823
870
  expect(result.enrollments).to.have.lengthOf(2);
824
871
  expect(result.enrollments).to.deep.equal([mockSiteEnrollment, mockEnrollment3]);
825
872
  });
873
+
874
+ it('should filter out enrollments with mismatching orgId', async () => {
875
+ const tierClientWithoutSite = new TierClient(
876
+ mockContext,
877
+ organizationInstance,
878
+ null,
879
+ productCode,
880
+ );
881
+
882
+ const mismatchingOrgId = 'different-org-id';
883
+ const mockEnrollment2 = {
884
+ getId: () => 'enrollment-456',
885
+ getSiteId: () => 'site-with-wrong-org',
886
+ getEntitlementId: () => 'entitlement-123',
887
+ };
888
+
889
+ const mockSiteForEnrollment1 = {
890
+ getId: () => siteId,
891
+ getOrganizationId: () => orgId,
892
+ };
893
+
894
+ const mockSiteForEnrollment2 = {
895
+ getId: () => 'site-with-wrong-org',
896
+ getOrganizationId: () => mismatchingOrgId,
897
+ };
898
+
899
+ mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
900
+ mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([
901
+ mockSiteEnrollment,
902
+ mockEnrollment2,
903
+ ]);
904
+ mockDataAccess.Site.batchGetByKeys.resolves({
905
+ data: [mockSiteForEnrollment1, mockSiteForEnrollment2],
906
+ unprocessed: [],
907
+ });
908
+
909
+ const result = await tierClientWithoutSite.getAllEnrollment();
910
+
911
+ expect(result.enrollments).to.have.lengthOf(1);
912
+ expect(result.enrollments[0].getId()).to.equal('enrollment-123');
913
+ });
914
+
915
+ it('should log warning when site not found for enrollment', async () => {
916
+ const tierClientWithoutSite = new TierClient(
917
+ mockContext,
918
+ organizationInstance,
919
+ null,
920
+ productCode,
921
+ );
922
+
923
+ mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
924
+ mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([mockSiteEnrollment]);
925
+ mockDataAccess.Site.batchGetByKeys.resolves({
926
+ data: [],
927
+ unprocessed: [],
928
+ });
929
+
930
+ const result = await tierClientWithoutSite.getAllEnrollment();
931
+
932
+ expect(result.enrollments).to.have.lengthOf(0);
933
+ expect(mockContext.log.warn).to.have.been.calledWith(
934
+ `Site not found for enrollment ${mockSiteEnrollment.getId()} with siteId ${siteId}`,
935
+ );
936
+ });
826
937
  });
827
938
 
828
939
  describe('getFirstEnrollment', () => {
@@ -834,10 +945,15 @@ describe('TierClient', () => {
834
945
  const mockSiteObject = {
835
946
  getId: () => siteId,
836
947
  getName: () => 'Test Site',
948
+ getOrganizationId: () => orgId,
837
949
  };
838
950
 
839
951
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
840
952
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([mockSiteEnrollment]);
953
+ mockDataAccess.Site.batchGetByKeys.resolves({
954
+ data: [mockSiteObject],
955
+ unprocessed: [],
956
+ });
841
957
  mockDataAccess.Site.findById.resolves(mockSiteObject);
842
958
 
843
959
  const tierClientWithoutSite = new TierClient(
@@ -861,6 +977,13 @@ describe('TierClient', () => {
861
977
  const mockSiteObject = {
862
978
  getId: () => siteId,
863
979
  getName: () => 'Test Site',
980
+ getOrganizationId: () => orgId,
981
+ };
982
+
983
+ const mockSiteObject2 = {
984
+ getId: () => 'other-site-id',
985
+ getName: () => 'Other Site',
986
+ getOrganizationId: () => orgId,
864
987
  };
865
988
 
866
989
  const mockEnrollment2 = {
@@ -874,6 +997,10 @@ describe('TierClient', () => {
874
997
  mockSiteEnrollment,
875
998
  mockEnrollment2,
876
999
  ]);
1000
+ mockDataAccess.Site.batchGetByKeys.resolves({
1001
+ data: [mockSiteObject, mockSiteObject2],
1002
+ unprocessed: [],
1003
+ });
877
1004
  mockDataAccess.Site.findById.resolves(mockSiteObject);
878
1005
 
879
1006
  const tierClientWithoutSite = new TierClient(
@@ -917,8 +1044,18 @@ describe('TierClient', () => {
917
1044
  });
918
1045
 
919
1046
  it('should return null site when site not found in database', async () => {
1047
+ const mockSiteObject = {
1048
+ getId: () => siteId,
1049
+ getName: () => 'Test Site',
1050
+ getOrganizationId: () => orgId,
1051
+ };
1052
+
920
1053
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
921
1054
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([mockSiteEnrollment]);
1055
+ mockDataAccess.Site.batchGetByKeys.resolves({
1056
+ data: [mockSiteObject],
1057
+ unprocessed: [],
1058
+ });
922
1059
  mockDataAccess.Site.findById.resolves(null);
923
1060
 
924
1061
  const tierClientWithoutSite = new TierClient(
@@ -951,10 +1088,15 @@ describe('TierClient', () => {
951
1088
  const mockSiteObject = {
952
1089
  getId: () => siteId,
953
1090
  getName: () => 'Test Site',
1091
+ getOrganizationId: () => orgId,
954
1092
  };
955
1093
 
956
1094
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
957
1095
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([mockSiteEnrollment]);
1096
+ mockDataAccess.Site.batchGetByKeys.resolves({
1097
+ data: [mockSiteObject],
1098
+ unprocessed: [],
1099
+ });
958
1100
  mockDataAccess.Site.findById.resolves(mockSiteObject);
959
1101
 
960
1102
  const result = await tierClient.getFirstEnrollment();
@@ -966,10 +1108,10 @@ describe('TierClient', () => {
966
1108
  });
967
1109
  });
968
1110
 
969
- it('should handle error when fetching site', async () => {
1111
+ it('should handle error when fetching site via batchGetByKeys', async () => {
970
1112
  mockDataAccess.Entitlement.findByOrganizationIdAndProductCode.resolves(mockEntitlement);
971
1113
  mockDataAccess.SiteEnrollment.allByEntitlementId.resolves([mockSiteEnrollment]);
972
- mockDataAccess.Site.findById.rejects(new Error('Site fetch error'));
1114
+ mockDataAccess.Site.batchGetByKeys.rejects(new Error('Site fetch error'));
973
1115
 
974
1116
  await expect(tierClient.getFirstEnrollment()).to.be.rejectedWith('Site fetch error');
975
1117
  });