@ampsec/platform-client 47.3.0 → 47.5.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.
Files changed (45) hide show
  1. package/build/src/dto/assetKeys.d.ts +2 -0
  2. package/build/src/dto/entityIdSummaries.dto.d.ts +22 -0
  3. package/build/src/dto/entityIdSummaries.dto.js +3 -0
  4. package/build/src/dto/entityIdSummaries.dto.js.map +1 -0
  5. package/build/src/dto/enums/globalUser.type.d.ts +1 -0
  6. package/build/src/dto/enums/globalUser.type.js +1 -0
  7. package/build/src/dto/enums/globalUser.type.js.map +1 -1
  8. package/build/src/dto/extKeyMap.dto.d.ts +2 -0
  9. package/build/src/dto/index.d.ts +1 -0
  10. package/build/src/dto/index.js +1 -0
  11. package/build/src/dto/index.js.map +1 -1
  12. package/build/src/services/AmpSdk.d.ts +8 -7
  13. package/build/src/services/AmpSdk.js +9 -8
  14. package/build/src/services/AmpSdk.js.map +1 -1
  15. package/build/src/services/entity.service.d.ts +12 -10
  16. package/build/src/services/entity.service.js +81 -16
  17. package/build/src/services/entity.service.js.map +1 -1
  18. package/build/src/services/index.d.ts +3 -0
  19. package/build/src/services/index.js +3 -0
  20. package/build/src/services/index.js.map +1 -1
  21. package/build/src/services/lookup.service.d.ts +55 -0
  22. package/build/src/services/lookup.service.js +173 -0
  23. package/build/src/services/lookup.service.js.map +1 -0
  24. package/build/src/services/rest/RestClient.d.ts +2 -0
  25. package/build/src/services/rest/RestClient.js +1 -0
  26. package/build/src/services/rest/RestClient.js.map +1 -1
  27. package/build/src/services/saasEntity.service.d.ts +32 -0
  28. package/build/src/services/saasEntity.service.js +154 -0
  29. package/build/src/services/saasEntity.service.js.map +1 -0
  30. package/build/src/services/utils.d.ts +1 -0
  31. package/build/src/services/utils.js +15 -0
  32. package/build/src/services/utils.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/dto/assetKeys.ts +2 -0
  35. package/src/dto/entityIdSummaries.dto.ts +27 -0
  36. package/src/dto/enums/globalUser.type.ts +1 -0
  37. package/src/dto/extKeyMap.dto.ts +2 -0
  38. package/src/dto/index.ts +1 -0
  39. package/src/services/AmpSdk.ts +12 -29
  40. package/src/services/entity.service.ts +87 -24
  41. package/src/services/index.ts +3 -0
  42. package/src/services/lookup.service.ts +176 -0
  43. package/src/services/rest/RestClient.ts +3 -0
  44. package/src/services/saasEntity.service.ts +181 -0
  45. package/src/services/utils.ts +8 -0
@@ -0,0 +1,176 @@
1
+ import _ from 'lodash';
2
+ import {AssetIdDto, AssetKeys, RawUserIds, SaasComponentIdDto, UserIdDto} from '../dto';
3
+ import {formatMacAddress} from './utils';
4
+
5
+ export class UserLookupService {
6
+ private readonly emailLookupMap = new Map<string, UserIdDto>();
7
+ private readonly extIdLookupMap = new Map<string, UserIdDto>();
8
+ private lastUpdated = new Date('2023-04-17T13:00:00.000Z');
9
+
10
+ getLastUpdated(): string {
11
+ return this.lastUpdated.toISOString();
12
+ }
13
+
14
+ // TODO throw errors if conflicts are found
15
+ /**
16
+ * Emails are normalized to lower case but external IDs are case sensitive
17
+ */
18
+ add(user: UserIdDto) {
19
+ user.emails.forEach(email => this.emailLookupMap.set(email.toLowerCase(), user));
20
+ if (user.extId) {
21
+ this.extIdLookupMap.set(user.extId, user);
22
+ }
23
+ this.lastUpdated = new Date();
24
+ }
25
+
26
+ // TODO throw errors if conflicts are found
27
+ /**
28
+ * Calls through to `byEmails` and `byExtId`
29
+ */
30
+ lookup(userIds?: RawUserIds): UserIdDto[] {
31
+ if (!userIds) {
32
+ return [];
33
+ }
34
+ const matches = [] as UserIdDto[];
35
+ const emails = [];
36
+ if (userIds.primaryEmail) {
37
+ emails.push(userIds.primaryEmail);
38
+ }
39
+ if (Array.isArray(userIds.emails)) {
40
+ emails.push(...userIds.emails);
41
+ }
42
+ matches.push(...this.byEmails(emails));
43
+ if (userIds.extId) {
44
+ const match = this.byExtId(userIds.extId);
45
+ if (match) {
46
+ matches.push(match);
47
+ }
48
+ }
49
+ return _.uniqBy(matches, 'id');
50
+ }
51
+
52
+ /**
53
+ * Emails are normalized to lower case
54
+ */
55
+ byEmails(emails: string[]): UserIdDto[] {
56
+ const matches = [] as UserIdDto[];
57
+ if (!Array.isArray(emails)) {
58
+ emails = [emails];
59
+ }
60
+
61
+ emails = _.uniq(emails);
62
+
63
+ for (const email of emails) {
64
+ const match = this.emailLookupMap.get(email.toLocaleLowerCase());
65
+ if (match) {
66
+ matches.push(match);
67
+ }
68
+ }
69
+ return _.uniqBy(matches, 'id');
70
+ }
71
+ /**
72
+ * Emails are normalized to lower case
73
+ */
74
+ byEmail(email: string): UserIdDto | undefined {
75
+ return this.emailLookupMap.get(email.toLocaleLowerCase());
76
+ }
77
+
78
+ /**
79
+ * External IDs are case sensitive
80
+ */
81
+ byExtId(extId: string): UserIdDto | undefined {
82
+ return this.extIdLookupMap.get(extId);
83
+ }
84
+ }
85
+
86
+ export class AssetLookupService {
87
+ private readonly macsLookupMap = new Map<string, AssetIdDto>();
88
+ private readonly snLookupMap = new Map<string, AssetIdDto>();
89
+ private readonly extIdLookupMap = new Map<string, AssetIdDto>();
90
+ private lastUpdated = new Date('2023-04-17T13:00:00.000Z');
91
+
92
+ getLastUpdated(): string {
93
+ return this.lastUpdated.toISOString();
94
+ }
95
+
96
+ // TODO throw errors if conflicts are found
97
+ /**
98
+ * Macs are normalized to standard format using `formatMacAddress` but serial numbers and external IDs are case sensitive
99
+ */
100
+ add(asset: AssetIdDto) {
101
+ asset.macs?.forEach(mac => this.macsLookupMap.set(formatMacAddress(mac), asset));
102
+ if (asset.sn) {
103
+ this.snLookupMap.set(asset.sn, asset);
104
+ }
105
+ if (asset.extId) {
106
+ this.extIdLookupMap.set(asset.extId, asset);
107
+ }
108
+ this.lastUpdated = new Date();
109
+ }
110
+
111
+ // TODO throw errors if conflicts are found
112
+ lookup(keys: AssetKeys): AssetIdDto[] {
113
+ const matches = [] as AssetIdDto[];
114
+ matches.push(...this.byMacs(keys.macs));
115
+ if (keys.sn) {
116
+ const ids = this.bySn(keys.sn);
117
+ if (ids) {
118
+ matches.push(ids);
119
+ }
120
+ }
121
+ if (keys.extId) {
122
+ const match = this.byExtId(keys.extId);
123
+ if (match) {
124
+ matches.push(match);
125
+ }
126
+ }
127
+ return _.uniqBy(matches, 'id');
128
+ }
129
+
130
+ /**
131
+ * Macs are normalized to standard format using `formatMacAddress`
132
+ */
133
+ byMacs(macs: string | string[]): AssetIdDto[] {
134
+ const matches = [] as AssetIdDto[];
135
+ if (!Array.isArray(macs)) {
136
+ macs = [macs];
137
+ }
138
+ for (const mac of macs) {
139
+ const match = this.macsLookupMap.get(formatMacAddress(mac));
140
+ if (match) {
141
+ matches.push(match);
142
+ }
143
+ }
144
+ return _.uniqBy(matches, 'id');
145
+ }
146
+
147
+ bySn(sn?: string): AssetIdDto | undefined {
148
+ return sn ? this.snLookupMap.get(sn) : undefined;
149
+ }
150
+
151
+ /**
152
+ * External IDs are case sensitive
153
+ */
154
+ byExtId(extId: string): AssetIdDto | undefined {
155
+ return this.extIdLookupMap.get(extId);
156
+ }
157
+ }
158
+
159
+ export class SaasComponentLookupService {
160
+ private readonly extIdLookupMap = new Map<string, SaasComponentIdDto>();
161
+ private lastUpdated = new Date('2023-04-17T13:00:00.000Z');
162
+
163
+ getLastUpdated(): string {
164
+ return this.lastUpdated.toISOString();
165
+ }
166
+
167
+ // TODO throw errors if conflicts are found
168
+ add(sc: SaasComponentIdDto) {
169
+ this.extIdLookupMap.set(sc.extId, sc);
170
+ this.lastUpdated = new Date();
171
+ }
172
+
173
+ byExtId(extId: string): SaasComponentIdDto | undefined {
174
+ return this.extIdLookupMap.get(extId);
175
+ }
176
+ }
@@ -16,6 +16,8 @@ export type AmpRestClientOptions = {
16
16
  rateLimitStrategy?: RestClientRateLimitStrategy;
17
17
  logger?: AmpLogger;
18
18
  client?: AxiosStatic;
19
+ /** Throws errors on non-200 responses if `true`. Default `false` */
20
+ strict?: boolean;
19
21
  };
20
22
 
21
23
  export type RestClientOptions = {
@@ -118,6 +120,7 @@ export const getAmpRestClient = (options: AmpRestClientOptions): RestClient => {
118
120
  rateLimitStrategy: new StaticRestClientRateLimitStrategy(50),
119
121
  logger: options.logger,
120
122
  client: options.client,
123
+ strict: options.strict,
121
124
  });
122
125
  return ampClient;
123
126
  };
@@ -0,0 +1,181 @@
1
+ import _ from 'lodash';
2
+ import {
3
+ AssetIdDto,
4
+ BaseDto,
5
+ BaseUpsertDto,
6
+ ExtKeyMap,
7
+ Page,
8
+ PlatformSaasAssetDto,
9
+ PlatformSaasAssetUpsertDto,
10
+ PlatformSaasComponentDto,
11
+ PlatformSaasComponentUpsertDto,
12
+ PlatformSaasUserDto,
13
+ PlatformSaasUserUpsertDto,
14
+ SaasComponentIdDto,
15
+ UserIdDto,
16
+ } from '../dto';
17
+ import {TargetApi, TARGET_API_AGENT, ErrorHandler, KIND} from './constants';
18
+ import {AmpDataService} from './data.service';
19
+ import {RestClient, RestRequest} from './rest';
20
+ import {QueryMap} from './rest/utils';
21
+ import {AssetLookupService, SaasComponentLookupService, UserLookupService} from './lookup.service';
22
+ import {AmpEntityServiceImpl, EntityCallOptions} from './entity.service';
23
+
24
+ const LOOKUP_ID_PAGE_SIZE = 50;
25
+
26
+ export interface AmpSaaSEntityService<WriteT extends BaseUpsertDto, ReadT extends BaseDto> extends AmpDataService<ReadT> {
27
+ create(_model: WriteT | WriteT[], _options?: EntityCallOptions): Promise<Page<ReadT>>;
28
+ update(_model: WriteT | WriteT[], _options?: EntityCallOptions): Promise<Page<ReadT>>;
29
+ delete(_id: string, _options?: EntityCallOptions): Promise<Page<ReadT>>;
30
+ getLookupIds(_cid: string, _options?: EntityCallOptions): Promise<ExtKeyMap>;
31
+ }
32
+
33
+ const extIdMapErrorHandler: ErrorHandler<ExtKeyMap> = (error: unknown) => {
34
+ if (error instanceof Error) {
35
+ console.error(error.message);
36
+ }
37
+ return {};
38
+ };
39
+
40
+ export class AmpSaaSEntityServiceImpl<WriteT extends BaseUpsertDto, ReadT extends BaseDto>
41
+ extends AmpEntityServiceImpl<WriteT, ReadT>
42
+ implements AmpSaaSEntityService<WriteT, ReadT>
43
+ {
44
+ constructor(rest: RestClient, kind: string, targetApi: TargetApi = TARGET_API_AGENT) {
45
+ super(rest, kind, targetApi);
46
+ }
47
+
48
+ /** @deprecated */
49
+ getLookupIds = (cid: string, options?: EntityCallOptions): Promise<ExtKeyMap> => {
50
+ const req: RestRequest = {
51
+ url: `/${this.targetApi}/v1/${this.kind}/ext_key_map`,
52
+ method: 'GET',
53
+ params: {
54
+ ...((options?.params as QueryMap) ?? {}),
55
+ cid,
56
+ },
57
+ };
58
+ return this.call(req, extIdMapErrorHandler);
59
+ };
60
+ }
61
+
62
+ const errorHandler = (error: unknown) => {
63
+ if (error instanceof Error) {
64
+ console.error(error.message);
65
+ }
66
+ throw error;
67
+ };
68
+
69
+ export class AmpSdkSaasUserService extends AmpSaaSEntityServiceImpl<PlatformSaasUserUpsertDto, PlatformSaasUserDto> {
70
+ constructor(rest: RestClient, targetApi: TargetApi = TARGET_API_AGENT) {
71
+ super(rest, KIND.SAAS_USERS, targetApi);
72
+ }
73
+
74
+ getLookup = async (cid: string, options?: EntityCallOptions): Promise<UserLookupService> => {
75
+ const lookup = new UserLookupService();
76
+ return this.refreshLookup(lookup, cid, options);
77
+ };
78
+
79
+ refreshLookup = async (lookup: UserLookupService, cid: string, options?: EntityCallOptions): Promise<UserLookupService> => {
80
+ const limit = LOOKUP_ID_PAGE_SIZE;
81
+ let offset = 0;
82
+ let hasMore = true;
83
+ if (!options) {
84
+ options = {params: {}};
85
+ }
86
+ _.set(options, 'params.updatedAt', {$gt: lookup.getLastUpdated()});
87
+ while (hasMore) {
88
+ const req: RestRequest = {
89
+ url: `/${this.targetApi}/v1/${this.kind}/id_summary`,
90
+ method: 'GET',
91
+ params: {
92
+ ...((options?.params as QueryMap) ?? {}),
93
+ cid,
94
+ limit,
95
+ offset,
96
+ },
97
+ };
98
+ const page = await this.call(req, errorHandler as ErrorHandler<Page<UserIdDto>>);
99
+ page.data.forEach(ids => lookup.add(ids));
100
+ offset = (page.hints.offset ?? offset) + limit;
101
+ hasMore = page.hints.hasMore ?? false;
102
+ }
103
+ return lookup;
104
+ };
105
+ }
106
+
107
+ export class AmpSdkSaasAssetService extends AmpSaaSEntityServiceImpl<PlatformSaasAssetUpsertDto, PlatformSaasAssetDto> {
108
+ constructor(rest: RestClient, targetApi: TargetApi = TARGET_API_AGENT) {
109
+ super(rest, KIND.SAAS_ASSETS, targetApi);
110
+ }
111
+
112
+ getLookup = async (cid: string, options?: EntityCallOptions): Promise<AssetLookupService> => {
113
+ const lookup = new AssetLookupService();
114
+ return this.refreshLookup(lookup, cid, options);
115
+ };
116
+
117
+ refreshLookup = async (lookup: AssetLookupService, cid: string, options?: EntityCallOptions): Promise<AssetLookupService> => {
118
+ const limit = LOOKUP_ID_PAGE_SIZE;
119
+ let offset = 0;
120
+ let hasMore = true;
121
+ if (!options) {
122
+ options = {params: {}};
123
+ }
124
+ _.set(options, 'params.updatedAt', {$gt: lookup.getLastUpdated()});
125
+ while (hasMore) {
126
+ const req: RestRequest = {
127
+ url: `/${this.targetApi}/v1/${this.kind}/id_summary`,
128
+ method: 'GET',
129
+ params: {
130
+ ...((options?.params as QueryMap) ?? {}),
131
+ cid,
132
+ limit,
133
+ offset,
134
+ },
135
+ };
136
+ const page = await this.call(req, errorHandler as ErrorHandler<Page<AssetIdDto>>);
137
+ page.data.forEach(ids => lookup.add(ids));
138
+ offset = (page.hints.offset ?? offset) + limit;
139
+ hasMore = page.hints.hasMore ?? false;
140
+ }
141
+ return lookup;
142
+ };
143
+ }
144
+
145
+ export class AmpSdkSaasComponentService extends AmpSaaSEntityServiceImpl<PlatformSaasComponentUpsertDto, PlatformSaasComponentDto> {
146
+ constructor(rest: RestClient, targetApi: TargetApi = TARGET_API_AGENT) {
147
+ super(rest, KIND.SAAS_COMPONENTS, targetApi);
148
+ }
149
+
150
+ getLookup = async (cid: string, options?: EntityCallOptions): Promise<SaasComponentLookupService> => {
151
+ const lookup = new SaasComponentLookupService();
152
+ return this.refreshLookup(lookup, cid, options);
153
+ };
154
+
155
+ refreshLookup = async (lookup: SaasComponentLookupService, cid: string, options?: EntityCallOptions): Promise<SaasComponentLookupService> => {
156
+ const limit = LOOKUP_ID_PAGE_SIZE;
157
+ let offset = 0;
158
+ let hasMore = true;
159
+ if (!options) {
160
+ options = {params: {}};
161
+ }
162
+ _.set(options, 'params.updatedAt', {$gt: lookup.getLastUpdated()});
163
+ while (hasMore) {
164
+ const req: RestRequest = {
165
+ url: `/${this.targetApi}/v1/${this.kind}/id_summary`,
166
+ method: 'GET',
167
+ params: {
168
+ ...((options?.params as QueryMap) ?? {}),
169
+ cid,
170
+ limit,
171
+ offset,
172
+ },
173
+ };
174
+ const page = await this.call(req, errorHandler as ErrorHandler<Page<SaasComponentIdDto>>);
175
+ page.data.forEach(ids => lookup.add(ids));
176
+ offset = (page.hints.offset ?? offset) + limit;
177
+ hasMore = page.hints.hasMore ?? false;
178
+ }
179
+ return lookup;
180
+ };
181
+ }
@@ -0,0 +1,8 @@
1
+ import _ from 'lodash';
2
+
3
+ export function formatMacAddress(raw: string): string {
4
+ const lower = raw.toLowerCase().replace(/[^a-f0-9]/g, '');
5
+ return _.chunk(lower.split(''), 2)
6
+ .map(chunk => chunk.join(''))
7
+ .join(':');
8
+ }