@ampsec/platform-client 4.0.0 → 4.0.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/package.json +3 -2
- package/src/FilterCriteria.ts +18 -0
- package/src/dto/agents.dto.ts +15 -0
- package/src/dto/assets.dto.ts +10 -0
- package/src/dto/base.dto.ts +25 -0
- package/src/dto/connectors.dto.ts +13 -0
- package/src/dto/enums/agent.status.ts +6 -0
- package/src/dto/enums/connector.status.ts +7 -0
- package/src/dto/enums/finding.severity.ts +7 -0
- package/src/dto/enums/finding.status.ts +5 -0
- package/src/dto/enums/index.ts +4 -0
- package/src/dto/extKeyMap.dto.ts +6 -0
- package/src/dto/findings.dto.ts +26 -0
- package/src/dto/index.ts +19 -0
- package/src/dto/message.dto.ts +3 -0
- package/src/dto/metrics.dto.ts +10 -0
- package/src/dto/page.dto.ts +14 -0
- package/src/dto/platform/index.ts +13 -0
- package/src/dto/platform/multi.tenant.based.dto.ts +11 -0
- package/src/dto/platform/platform.agents.dto.ts +6 -0
- package/src/dto/platform/platform.assets.dto.ts +6 -0
- package/src/dto/platform/platform.connectors.dto.ts +6 -0
- package/src/dto/platform/platform.findings.dto.ts +6 -0
- package/src/dto/platform/platform.metrics.dto.ts +6 -0
- package/src/dto/platform/platform.providers.dto.ts +5 -0
- package/src/dto/platform/platform.reportResults.dto.ts +6 -0
- package/src/dto/platform/platform.saasAssets.dto.ts +5 -0
- package/src/dto/platform/platform.saasComponents.dto.ts +6 -0
- package/src/dto/platform/platform.saasUsers.dto.ts +6 -0
- package/src/dto/platform/platform.tenants.dto.ts +5 -0
- package/src/dto/platform/platform.users.dto.ts +6 -0
- package/src/dto/platform/tenant.based.dto.ts +11 -0
- package/src/dto/providers.dto.ts +8 -0
- package/src/dto/reportResults.dto.ts +12 -0
- package/src/dto/riskContributors.dto.ts +10 -0
- package/src/dto/saasAssets.dto.ts +16 -0
- package/src/dto/saasComponents.dto.ts +18 -0
- package/src/dto/saasUsers.dto.ts +14 -0
- package/src/dto/tenants.dto.ts +8 -0
- package/src/dto/users.dto.ts +28 -0
- package/src/etag.ts +17 -0
- package/src/index.ts +4 -0
- package/src/services/AmpApi.ts +66 -0
- package/src/services/AmpSdk.ts +72 -0
- package/src/services/data.service.ts +69 -0
- package/src/services/entity.service.ts +104 -0
- package/src/services/index.ts +5 -0
- package/src/services/rest/RestClient.ts +103 -0
- package/src/services/rest/RestRequest.ts +9 -0
- package/src/services/rest/RestResponse.ts +8 -0
- package/src/services/rest/index.ts +5 -0
- package/src/services/rest/rateLimit.rest.ts +31 -0
- package/src/services/rest/retry.rest.ts +21 -0
- package/src/services/rest/utils.ts +13 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ampsec/platform-client",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"typescript": "^5.0.4"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
|
-
"/build/src"
|
|
43
|
+
"/build/src",
|
|
44
|
+
"/src"
|
|
44
45
|
]
|
|
45
46
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type FilterCriteria = {
|
|
2
|
+
/** Number of records to return. */
|
|
3
|
+
limit?: number;
|
|
4
|
+
/** Number of records to skip before capturing results. */
|
|
5
|
+
offset?: number;
|
|
6
|
+
/** Tenant ID */
|
|
7
|
+
tid?: number;
|
|
8
|
+
/** Connector ID */
|
|
9
|
+
cid?: number;
|
|
10
|
+
/** Organization name. Defaults to `*` which matches all organizations. */
|
|
11
|
+
organization?: '*' | 'EXTERNAL' | string;
|
|
12
|
+
/** Department name. Defaults to `*` which matches all departments. */
|
|
13
|
+
department?: '*' | string;
|
|
14
|
+
/** User ID */
|
|
15
|
+
uid?: number;
|
|
16
|
+
/** Asset ID */
|
|
17
|
+
aid?: number;
|
|
18
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {AgentStatus} from './enums/agent.status';
|
|
2
|
+
import {BaseDto, BaseUpsertDto} from './base.dto';
|
|
3
|
+
|
|
4
|
+
export type AgentUpsertDto = BaseUpsertDto & {
|
|
5
|
+
/** Agent first name */
|
|
6
|
+
firstName: string;
|
|
7
|
+
/** Agent last name */
|
|
8
|
+
lastName: string;
|
|
9
|
+
/** Status of the Agent, e.g. ACTIVE */
|
|
10
|
+
status: AgentStatus;
|
|
11
|
+
/** External Id */
|
|
12
|
+
email: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type AgentDto = BaseDto & AgentUpsertDto;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type AssetUpsertDto = ChangeAwareUpsertDto & {
|
|
4
|
+
/** External Id */
|
|
5
|
+
sn: string;
|
|
6
|
+
/** User Id of the asset owner */
|
|
7
|
+
uid?: number | undefined;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type AssetDto = ChangeAwareDto & AssetUpsertDto;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type BaseUpsertDto = {
|
|
2
|
+
/** Unique Row Identifier. Populated for entity updates. */
|
|
3
|
+
id?: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type BaseDto = {
|
|
7
|
+
/** Unique Row Identifier */
|
|
8
|
+
id: number;
|
|
9
|
+
/** Record creation date in database. */
|
|
10
|
+
createdAt: string;
|
|
11
|
+
/** Last record update date in database. */
|
|
12
|
+
updatedAt: string;
|
|
13
|
+
/** Record deletion date in database. Only present on soft deleted, i.e. `null => active` */
|
|
14
|
+
deletedAt: string | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ChangeAwareUpsertDto = BaseUpsertDto & {
|
|
18
|
+
/** MD5 hash of a subset of properties to detect field level changes. */
|
|
19
|
+
etag: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type ChangeAwareDto = BaseDto & {
|
|
23
|
+
/** MD5 hash of a subset of properties to deted field level changes. */
|
|
24
|
+
etag: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto} from './base.dto';
|
|
2
|
+
import {ConnectorStatus} from './enums/connector.status';
|
|
3
|
+
|
|
4
|
+
export type ConnectorUpsertDto = BaseUpsertDto & {
|
|
5
|
+
/** Human readable name of the connector */
|
|
6
|
+
displayValue: string;
|
|
7
|
+
/** Status of the connector, e.g. ACTIVE */
|
|
8
|
+
status: ConnectorStatus;
|
|
9
|
+
/** Provider Id */
|
|
10
|
+
pid?: number | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ConnectorDto = BaseDto & ConnectorUpsertDto;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto} from './base.dto';
|
|
2
|
+
import {FindingSeverity} from './enums/finding.severity';
|
|
3
|
+
import {FindingStatus} from './enums/finding.status';
|
|
4
|
+
|
|
5
|
+
export type FindingUpsertDto = BaseUpsertDto & {
|
|
6
|
+
/** Date the finding was closed */
|
|
7
|
+
closedAt?: string;
|
|
8
|
+
/** Type of finding */
|
|
9
|
+
kind: string;
|
|
10
|
+
/** Status of the finding, e.g. OPEN */
|
|
11
|
+
status: FindingStatus;
|
|
12
|
+
/** Severity of the finding, e.g. CRITICAL, HIGH, etc... */
|
|
13
|
+
severity: FindingSeverity;
|
|
14
|
+
/** Number of times the user has been engaged to resolve the finding */
|
|
15
|
+
numberOfEngagements: number;
|
|
16
|
+
/** Human readable name of the finding */
|
|
17
|
+
displayValue: string;
|
|
18
|
+
/** Description of the finding */
|
|
19
|
+
description?: string;
|
|
20
|
+
/** Id of the user to which the finding applies */
|
|
21
|
+
uid?: number | undefined;
|
|
22
|
+
/** Id of the SaasComponent related to the given finding */
|
|
23
|
+
scid: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type FindingDto = BaseDto & FindingUpsertDto;
|
package/src/dto/index.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export * from './agents.dto';
|
|
2
|
+
export * from './assets.dto';
|
|
3
|
+
export * from './base.dto';
|
|
4
|
+
export * from './connectors.dto';
|
|
5
|
+
export * from './enums';
|
|
6
|
+
export * from './extKeyMap.dto';
|
|
7
|
+
export * from './findings.dto';
|
|
8
|
+
export * from './message.dto';
|
|
9
|
+
export * from './metrics.dto';
|
|
10
|
+
export * from './page.dto';
|
|
11
|
+
export * from './platform';
|
|
12
|
+
export * from './providers.dto';
|
|
13
|
+
export * from './reportResults.dto';
|
|
14
|
+
export * from './riskContributors.dto';
|
|
15
|
+
export * from './saasAssets.dto';
|
|
16
|
+
export * from './saasComponents.dto';
|
|
17
|
+
export * from './saasUsers.dto';
|
|
18
|
+
export * from './tenants.dto';
|
|
19
|
+
export * from './users.dto';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Page<T> = {
|
|
2
|
+
kind: string;
|
|
3
|
+
data: T[];
|
|
4
|
+
error: string | null;
|
|
5
|
+
hints: {
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
hasMore?: boolean;
|
|
9
|
+
next?: string;
|
|
10
|
+
count?: number;
|
|
11
|
+
links?: {[keyName: string]: string};
|
|
12
|
+
[propName: string]: number | string | boolean | null | {[keyName: string]: string} | undefined;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './platform.agents.dto';
|
|
2
|
+
export * from './platform.assets.dto';
|
|
3
|
+
export * from './platform.connectors.dto';
|
|
4
|
+
export * from './platform.findings.dto';
|
|
5
|
+
export * from './platform.metrics.dto';
|
|
6
|
+
export * from './platform.providers.dto';
|
|
7
|
+
export * from './platform.reportResults.dto';
|
|
8
|
+
export * from './platform.saasAssets.dto';
|
|
9
|
+
export * from './platform.saasComponents.dto';
|
|
10
|
+
export * from './platform.saasUsers.dto';
|
|
11
|
+
export * from './platform.tenants.dto';
|
|
12
|
+
export * from './platform.users.dto';
|
|
13
|
+
export * from './tenant.based.dto';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto} from '../base.dto';
|
|
2
|
+
|
|
3
|
+
export type UpsertMultiTenantBased = BaseUpsertDto & {
|
|
4
|
+
/** Tenant Ids for multi-tenant entities */
|
|
5
|
+
tids?: number[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type MultiTenantBased = BaseDto & {
|
|
9
|
+
/** Tenant Ids for multi-tenant entities */
|
|
10
|
+
tids: number[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {AgentDto, AgentUpsertDto} from '../agents.dto';
|
|
2
|
+
import {MultiTenantBased, UpsertMultiTenantBased} from './multi.tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformAgentUpsertDto = AgentUpsertDto & UpsertMultiTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformAgentDto = AgentDto & MultiTenantBased;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {ConnectorDto, ConnectorUpsertDto} from '../connectors.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformConnectorUpsertDto = ConnectorUpsertDto & UpsertTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformConnectorDto = ConnectorDto & TenantBased;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {FindingDto, FindingUpsertDto} from '../findings.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformFindingUpsertDto = FindingUpsertDto & UpsertTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformFindingDto = FindingDto & TenantBased;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {ReportResultDto, ReportResultUpsertDto} from '../reportResults.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformReportResultUpsertDto = ReportResultUpsertDto & UpsertTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformReportResultDto = ReportResultDto & TenantBased;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import {SaasAssetDto, SaasAssetUpsertDto} from '../saasAssets.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformSaasAssetUpsertDto = SaasAssetUpsertDto & UpsertTenantBased;
|
|
5
|
+
export type PlatformSaasAssetDto = SaasAssetDto & TenantBased;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {SaasComponentDto, SaasComponentUpsertDto} from '../saasComponents.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformSaasComponentUpsertDto = SaasComponentUpsertDto & UpsertTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformSaasComponentDto = SaasComponentDto & TenantBased;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {SaasUserUpsertDto} from '../saasUsers.dto';
|
|
2
|
+
import {TenantBased, UpsertTenantBased} from './tenant.based.dto';
|
|
3
|
+
|
|
4
|
+
export type PlatformSaasUserUpsertDto = SaasUserUpsertDto & UpsertTenantBased;
|
|
5
|
+
|
|
6
|
+
export type PlatformSaasUserDto = SaasUserUpsertDto & TenantBased;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type ReportResultUpsertDto = BaseUpsertDto & {
|
|
4
|
+
/** External Id */
|
|
5
|
+
extKey: string;
|
|
6
|
+
/** Timestamp the report result was generated in ISO8601 format */
|
|
7
|
+
ts: string;
|
|
8
|
+
/** The results generated for the given report */
|
|
9
|
+
results: unknown;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ReportResultDto = BaseDto & ReportResultUpsertDto;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type RiskContributorUpsertDto = BaseUpsertDto & {
|
|
4
|
+
/** Short descriptor of the risk contributor, e.g. V, P, etc... */
|
|
5
|
+
displayValue: string;
|
|
6
|
+
/** Description of the risk contributor */
|
|
7
|
+
description: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type RiskContributorDto = BaseDto & RiskContributorUpsertDto;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type SaasAssetUpsertDto = ChangeAwareUpsertDto & {
|
|
4
|
+
/** External Id */
|
|
5
|
+
sn: string;
|
|
6
|
+
/** Connector Id */
|
|
7
|
+
cid: number;
|
|
8
|
+
/** Asset Id */
|
|
9
|
+
aid: number;
|
|
10
|
+
/** User Id */
|
|
11
|
+
uid?: number | undefined;
|
|
12
|
+
/** Information specific to this asset */
|
|
13
|
+
meta: unknown;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type SaasAssetDto = ChangeAwareDto & SaasAssetUpsertDto;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type SaasComponentUpsertDto = ChangeAwareUpsertDto & {
|
|
4
|
+
/** Asset Id */
|
|
5
|
+
aid?: number | undefined;
|
|
6
|
+
/** Connector Id */
|
|
7
|
+
cid: number;
|
|
8
|
+
/** */
|
|
9
|
+
kind: string;
|
|
10
|
+
/** Context specific information related to the Saas Component */
|
|
11
|
+
meta: unknown;
|
|
12
|
+
/** User Id of the affected user */
|
|
13
|
+
uid?: number | undefined;
|
|
14
|
+
/** External Id */
|
|
15
|
+
extKey: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type SaasComponentDto = ChangeAwareDto & SaasComponentUpsertDto;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './base.dto';
|
|
2
|
+
|
|
3
|
+
export type SaasUserUpsertDto = ChangeAwareUpsertDto & {
|
|
4
|
+
/** User Id */
|
|
5
|
+
uid: number;
|
|
6
|
+
/** Connector Id */
|
|
7
|
+
cid: number;
|
|
8
|
+
/** External Id */
|
|
9
|
+
email: string;
|
|
10
|
+
/** Additional information specific to this Saas User */
|
|
11
|
+
meta: unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type SaasUserDto = ChangeAwareDto & SaasUserUpsertDto;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './base.dto';
|
|
2
|
+
import {MetricDto} from './metrics.dto';
|
|
3
|
+
import {RiskContributorDto} from './riskContributors.dto';
|
|
4
|
+
|
|
5
|
+
export type UserUpsertDto = ChangeAwareUpsertDto & {
|
|
6
|
+
/** Tenant ID */
|
|
7
|
+
tid: number;
|
|
8
|
+
/** External ID: list of emails, allows or merging users across email aliases */
|
|
9
|
+
emails: string[];
|
|
10
|
+
/** User first name */
|
|
11
|
+
firstName: string;
|
|
12
|
+
/** User last name */
|
|
13
|
+
lastName: string;
|
|
14
|
+
/** Department to which the user is assigned */
|
|
15
|
+
department?: string;
|
|
16
|
+
/** User title */
|
|
17
|
+
title?: string;
|
|
18
|
+
/** Human readable name of the user's tenant, e.g. tenant.displayName */
|
|
19
|
+
organization?: string;
|
|
20
|
+
/** Employment start date used for calculating tenure */
|
|
21
|
+
startDate?: string;
|
|
22
|
+
/** Risk Contributors associated with the user */
|
|
23
|
+
riskContributors: RiskContributorDto[];
|
|
24
|
+
/** Metrics associated with the user */
|
|
25
|
+
metrics: MetricDto[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type UserDto = ChangeAwareDto & UserUpsertDto;
|
package/src/etag.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import md5 from 'blueimp-md5';
|
|
2
|
+
import {ChangeAwareDto, ChangeAwareUpsertDto} from './';
|
|
3
|
+
|
|
4
|
+
export function calculateEtag(model: unknown): string {
|
|
5
|
+
const hashObject: {etag?: string} = {...(model as object)};
|
|
6
|
+
delete hashObject.etag;
|
|
7
|
+
return md5(JSON.stringify(hashObject));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function withEtag(model: ChangeAwareDto | ChangeAwareUpsertDto) {
|
|
11
|
+
model.etag = calculateEtag(model);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function createWithEtag<T extends {etag: string}>(model: T): T {
|
|
15
|
+
model.etag = calculateEtag(model);
|
|
16
|
+
return model;
|
|
17
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentDto,
|
|
3
|
+
AgentUpsertDto,
|
|
4
|
+
AssetDto,
|
|
5
|
+
ConnectorDto,
|
|
6
|
+
ConnectorUpsertDto,
|
|
7
|
+
FindingDto,
|
|
8
|
+
ProviderDto,
|
|
9
|
+
ReportResultDto,
|
|
10
|
+
SaasAssetDto,
|
|
11
|
+
SaasComponentDto,
|
|
12
|
+
SaasUserDto,
|
|
13
|
+
TenantDto,
|
|
14
|
+
TenantUpsertDto,
|
|
15
|
+
UserDto,
|
|
16
|
+
} from '../dto';
|
|
17
|
+
import {AmpEntityService, AmpEntityServiceImpl} from './entity.service';
|
|
18
|
+
import {AmpDataService, AmpDataServiceImpl} from './data.service';
|
|
19
|
+
import {AmpRestClientOptions, RestClient, getAmpRestClient} from './rest';
|
|
20
|
+
|
|
21
|
+
export type AmpApiOptions = AmpRestClientOptions;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* AMP API
|
|
25
|
+
* This client is a wrapper around the AMP REST API meant to be used by
|
|
26
|
+
* agents, i.e. Amplifier Security's customers. If you are trying to implement
|
|
27
|
+
* a plugin leverage the AmpSdk instead (see {@link AmpSdkServices} ).
|
|
28
|
+
*
|
|
29
|
+
* This client provides READ_ONLY access to most of the AMP API. You will
|
|
30
|
+
* have write access to the following resources:
|
|
31
|
+
* - Agents: {@link AmpApi.agents}
|
|
32
|
+
* - Connectors: {@link AmpApi.connectors}
|
|
33
|
+
* - Tenants: {@link AmpApi.tenants}
|
|
34
|
+
*/
|
|
35
|
+
export class AmpApi {
|
|
36
|
+
readonly agents: AmpEntityService<AgentUpsertDto, AgentDto>;
|
|
37
|
+
readonly asset: AmpDataService<AssetDto>;
|
|
38
|
+
readonly connectors: AmpEntityService<ConnectorUpsertDto, ConnectorDto>;
|
|
39
|
+
readonly findings: AmpDataService<FindingDto>;
|
|
40
|
+
readonly providers: AmpDataService<ProviderDto>;
|
|
41
|
+
readonly reports: AmpDataService<ReportResultDto>;
|
|
42
|
+
readonly saasAssets: AmpDataService<SaasAssetDto>;
|
|
43
|
+
readonly saasComponents: AmpDataService<SaasComponentDto>;
|
|
44
|
+
readonly saasUsers: AmpDataService<SaasUserDto>;
|
|
45
|
+
readonly tenants: AmpEntityService<TenantUpsertDto, TenantDto>;
|
|
46
|
+
readonly users: AmpDataService<UserDto>;
|
|
47
|
+
|
|
48
|
+
constructor(rest: RestClient) {
|
|
49
|
+
this.agents = new AmpEntityServiceImpl<AgentUpsertDto, AgentDto>(rest, 'agents');
|
|
50
|
+
this.asset = new AmpDataServiceImpl<AssetDto>(rest, 'assets');
|
|
51
|
+
this.connectors = new AmpEntityServiceImpl<ConnectorUpsertDto, ConnectorDto>(rest, 'connectors');
|
|
52
|
+
this.findings = new AmpDataServiceImpl<FindingDto>(rest, 'findings');
|
|
53
|
+
this.providers = new AmpDataServiceImpl<ProviderDto>(rest, 'providers');
|
|
54
|
+
this.reports = new AmpDataServiceImpl<ReportResultDto>(rest, 'report_results');
|
|
55
|
+
this.saasAssets = new AmpDataServiceImpl<SaasAssetDto>(rest, 'saas_assets');
|
|
56
|
+
this.saasComponents = new AmpDataServiceImpl<SaasComponentDto>(rest, 'saas_components');
|
|
57
|
+
this.saasUsers = new AmpDataServiceImpl<SaasUserDto>(rest, 'saas_users');
|
|
58
|
+
this.tenants = new AmpEntityServiceImpl<TenantUpsertDto, TenantDto>(rest, 'agents');
|
|
59
|
+
this.users = new AmpDataServiceImpl<UserDto>(rest, 'users');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static instance(options: AmpApiOptions): AmpApi {
|
|
63
|
+
const rest = getAmpRestClient(options);
|
|
64
|
+
return new AmpApi(rest);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConnectorDto,
|
|
3
|
+
ConnectorUpsertDto,
|
|
4
|
+
PlatformAgentDto,
|
|
5
|
+
PlatformAgentUpsertDto,
|
|
6
|
+
PlatformAssetDto,
|
|
7
|
+
PlatformAssetUpsertDto,
|
|
8
|
+
PlatformFindingDto,
|
|
9
|
+
PlatformFindingUpsertDto,
|
|
10
|
+
PlatformReportResultDto,
|
|
11
|
+
PlatformReportResultUpsertDto,
|
|
12
|
+
PlatformSaasAssetDto,
|
|
13
|
+
PlatformSaasAssetUpsertDto,
|
|
14
|
+
PlatformSaasComponentDto,
|
|
15
|
+
PlatformSaasComponentUpsertDto,
|
|
16
|
+
PlatformSaasUserDto,
|
|
17
|
+
PlatformSaasUserUpsertDto,
|
|
18
|
+
PlatformUserDto,
|
|
19
|
+
PlatformUserUpsertDto,
|
|
20
|
+
ProviderDto,
|
|
21
|
+
TenantDto,
|
|
22
|
+
TenantUpsertDto,
|
|
23
|
+
} from '../dto';
|
|
24
|
+
import {AmpEntityService, AmpEntityServiceImpl, AmpGlobalEntityService, AmpGlobalEntityServiceImpl, AmpSaaSEntityService, AmpSaaSEntityServiceImpl} from './entity.service';
|
|
25
|
+
import {AmpDataService, AmpDataServiceImpl, TARGET_API_AGENT, TARGET_API_PLATFORM} from './data.service';
|
|
26
|
+
import {AmpRestClientOptions, RestClient, getAmpRestClient} from './rest';
|
|
27
|
+
|
|
28
|
+
export type AmpSdkOptions = AmpRestClientOptions;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* AMP API
|
|
32
|
+
* This client is a wrapper around the AMP REST API meant to be used by
|
|
33
|
+
* service accounts, i.e. plugins and platform extensions. If you are
|
|
34
|
+
* trying to implement a simple web client you should leverage the AmpApi
|
|
35
|
+
* instead (see {@link AmpApi} ).
|
|
36
|
+
*
|
|
37
|
+
* This client provides CRUD access to most of the AMP API. You
|
|
38
|
+
* have READ_ONLY access to the following resources:
|
|
39
|
+
* - providers: {@link AmpSdkServices.providers}
|
|
40
|
+
*/
|
|
41
|
+
export class AmpSdkServices {
|
|
42
|
+
readonly agents: AmpEntityService<PlatformAgentUpsertDto, PlatformAgentDto>;
|
|
43
|
+
readonly asset: AmpGlobalEntityService<PlatformAssetUpsertDto, PlatformAssetDto>;
|
|
44
|
+
readonly connectors: AmpEntityService<ConnectorUpsertDto, ConnectorDto>;
|
|
45
|
+
readonly findings: AmpSaaSEntityService<PlatformFindingUpsertDto, PlatformFindingDto>;
|
|
46
|
+
readonly providers: AmpDataService<ProviderDto>;
|
|
47
|
+
readonly reports: AmpSaaSEntityService<PlatformReportResultUpsertDto, PlatformReportResultDto>;
|
|
48
|
+
readonly saasAssets: AmpSaaSEntityService<PlatformSaasAssetUpsertDto, PlatformSaasAssetDto>;
|
|
49
|
+
readonly saasComponents: AmpSaaSEntityService<PlatformSaasComponentUpsertDto, PlatformSaasComponentDto>;
|
|
50
|
+
readonly saasUsers: AmpSaaSEntityService<PlatformSaasUserUpsertDto, PlatformSaasUserDto>;
|
|
51
|
+
readonly tenants: AmpEntityService<TenantUpsertDto, TenantDto>;
|
|
52
|
+
readonly users: AmpGlobalEntityService<PlatformUserUpsertDto, PlatformUserDto>;
|
|
53
|
+
|
|
54
|
+
constructor(rest: RestClient) {
|
|
55
|
+
this.agents = new AmpEntityServiceImpl<PlatformAgentUpsertDto, PlatformAgentDto>(rest, 'agents', TARGET_API_PLATFORM);
|
|
56
|
+
this.asset = new AmpGlobalEntityServiceImpl<PlatformAssetUpsertDto, PlatformAssetDto>(rest, 'assets', TARGET_API_PLATFORM);
|
|
57
|
+
this.connectors = new AmpEntityServiceImpl<ConnectorUpsertDto, ConnectorDto>(rest, 'connectors', TARGET_API_PLATFORM);
|
|
58
|
+
this.findings = new AmpSaaSEntityServiceImpl<PlatformFindingUpsertDto, PlatformFindingDto>(rest, 'findings', TARGET_API_PLATFORM);
|
|
59
|
+
this.providers = new AmpDataServiceImpl<ProviderDto>(rest, 'providers', TARGET_API_AGENT);
|
|
60
|
+
this.reports = new AmpSaaSEntityServiceImpl<PlatformReportResultUpsertDto, PlatformReportResultDto>(rest, 'report_results', TARGET_API_PLATFORM);
|
|
61
|
+
this.saasAssets = new AmpSaaSEntityServiceImpl<PlatformSaasAssetUpsertDto, PlatformSaasAssetDto>(rest, 'saas_assets', TARGET_API_PLATFORM);
|
|
62
|
+
this.saasComponents = new AmpSaaSEntityServiceImpl<PlatformSaasComponentUpsertDto, PlatformSaasComponentDto>(rest, 'saas_components', TARGET_API_PLATFORM);
|
|
63
|
+
this.saasUsers = new AmpSaaSEntityServiceImpl<PlatformSaasUserUpsertDto, PlatformSaasUserDto>(rest, 'saas_users', TARGET_API_PLATFORM);
|
|
64
|
+
this.tenants = new AmpEntityServiceImpl<TenantUpsertDto, TenantDto>(rest, 'tenants', TARGET_API_PLATFORM);
|
|
65
|
+
this.users = new AmpGlobalEntityServiceImpl<PlatformUserUpsertDto, PlatformUserDto>(rest, 'users', TARGET_API_PLATFORM);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static instance(options: AmpSdkOptions): AmpSdkServices {
|
|
69
|
+
const rest = getAmpRestClient(options);
|
|
70
|
+
return new AmpSdkServices(rest);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {BaseDto, Page} from '../dto';
|
|
2
|
+
import {FilterCriteria} from '../FilterCriteria';
|
|
3
|
+
import {RestClient, RestRequest} from './rest';
|
|
4
|
+
|
|
5
|
+
export interface AmpDataService<ReadT extends BaseDto> {
|
|
6
|
+
list(_filter: FilterCriteria): Promise<Page<ReadT>>;
|
|
7
|
+
getById(_id: number): Promise<Page<ReadT>>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ErrorHandler<T> {
|
|
11
|
+
(_error: unknown): T;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type TargetApi = 'api' | 'platform';
|
|
15
|
+
export const TARGET_API_PLATFORM: TargetApi = 'platform';
|
|
16
|
+
export const TARGET_API_AGENT: TargetApi = 'api';
|
|
17
|
+
|
|
18
|
+
export class AmpDataServiceImpl<ReadT extends BaseDto> implements AmpDataService<ReadT> {
|
|
19
|
+
protected readonly rest: RestClient;
|
|
20
|
+
protected readonly kind: string;
|
|
21
|
+
protected readonly targetApi: string;
|
|
22
|
+
|
|
23
|
+
constructor(rest: RestClient, kind: string, targetApi: TargetApi = 'api') {
|
|
24
|
+
this.rest = rest;
|
|
25
|
+
this.kind = kind;
|
|
26
|
+
this.targetApi = targetApi;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
protected async call<T>(req: RestRequest, errorHandler: ErrorHandler<T>): Promise<T> {
|
|
30
|
+
try {
|
|
31
|
+
const res = await this.rest.call(req);
|
|
32
|
+
return res.data as T;
|
|
33
|
+
} catch (error: unknown) {
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
console.error(error.message);
|
|
36
|
+
}
|
|
37
|
+
return errorHandler(error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected async getPage(req: RestRequest): Promise<Page<ReadT>> {
|
|
42
|
+
return this.call(req, (error: unknown) => {
|
|
43
|
+
if (error instanceof Error) {
|
|
44
|
+
console.error(error.message);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
data: [],
|
|
48
|
+
kind: this.kind.toUpperCase(),
|
|
49
|
+
error: (error as {message: string}).message,
|
|
50
|
+
hints: {},
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
list(filter: FilterCriteria): Promise<Page<ReadT>> {
|
|
56
|
+
return this.getPage({
|
|
57
|
+
url: `/${this.targetApi}/v1/${this.kind}`,
|
|
58
|
+
method: 'GET',
|
|
59
|
+
params: filter,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getById(id: number): Promise<Page<ReadT>> {
|
|
64
|
+
return this.getPage({
|
|
65
|
+
url: `/${this.targetApi}/v1/${this.kind}/${id}`,
|
|
66
|
+
method: 'GET',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {BaseDto, BaseUpsertDto, ExtKeyMap, Page} from '../dto';
|
|
2
|
+
import {AmpDataService, AmpDataServiceImpl, ErrorHandler, TARGET_API_AGENT, TargetApi} from './data.service';
|
|
3
|
+
import {RestClient, RestRequest} from './rest';
|
|
4
|
+
|
|
5
|
+
export interface AmpEntityService<WriteT extends BaseUpsertDto, ReadT extends BaseDto> extends AmpDataService<ReadT> {
|
|
6
|
+
create(_model: WriteT): Promise<Page<ReadT>>;
|
|
7
|
+
update(_model: WriteT): Promise<Page<ReadT>>;
|
|
8
|
+
delete(_id: number): Promise<Page<ReadT>>;
|
|
9
|
+
}
|
|
10
|
+
export interface AmpGlobalEntityService<WriteT extends BaseUpsertDto, ReadT extends BaseDto> extends AmpDataService<ReadT> {
|
|
11
|
+
create(_model: WriteT): Promise<Page<ReadT>>;
|
|
12
|
+
update(_model: WriteT): Promise<Page<ReadT>>;
|
|
13
|
+
delete(_id: number): Promise<Page<ReadT>>;
|
|
14
|
+
getLookupIds(_tid: number): Promise<ExtKeyMap>;
|
|
15
|
+
}
|
|
16
|
+
export interface AmpSaaSEntityService<WriteT extends BaseUpsertDto, ReadT extends BaseDto> extends AmpDataService<ReadT> {
|
|
17
|
+
create(_model: WriteT): Promise<Page<ReadT>>;
|
|
18
|
+
update(_model: WriteT): Promise<Page<ReadT>>;
|
|
19
|
+
delete(_id: number): Promise<Page<ReadT>>;
|
|
20
|
+
getLookupIds(_cid: number): Promise<ExtKeyMap>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AmpEntityServiceImpl<WriteT extends BaseUpsertDto, ReadT extends BaseDto> extends AmpDataServiceImpl<ReadT> implements AmpEntityService<WriteT, ReadT> {
|
|
24
|
+
constructor(rest: RestClient, kind: string, targetApi: TargetApi = TARGET_API_AGENT) {
|
|
25
|
+
super(rest, kind, targetApi);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
create(model: WriteT): Promise<Page<ReadT>> {
|
|
29
|
+
return this.getPage({
|
|
30
|
+
url: `/${this.targetApi}/v1/${this.kind}`,
|
|
31
|
+
method: 'POST',
|
|
32
|
+
data: model,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
update(model: WriteT): Promise<Page<ReadT>> {
|
|
37
|
+
return this.getPage({
|
|
38
|
+
url: `/${this.targetApi}/v1/${this.kind}/${model.id}`,
|
|
39
|
+
method: 'PUT',
|
|
40
|
+
data: model,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async delete(id: number): Promise<Page<ReadT>> {
|
|
45
|
+
const res = await this.getById(id);
|
|
46
|
+
if (res.data.length === 1) {
|
|
47
|
+
const req: RestRequest = {
|
|
48
|
+
url: `/${this.targetApi}/v1/${this.kind}/${id}`,
|
|
49
|
+
method: 'DELETE',
|
|
50
|
+
};
|
|
51
|
+
const {error} = await this.call(req, (error: unknown) => {
|
|
52
|
+
if (error instanceof Error) {
|
|
53
|
+
console.error(error.message);
|
|
54
|
+
}
|
|
55
|
+
return {success: false, error};
|
|
56
|
+
});
|
|
57
|
+
if (error) {
|
|
58
|
+
throw new Error(`Failed to delete ${this.kind} with id="${id}"`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return res;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const extIdMapErrorHandler: ErrorHandler<ExtKeyMap> = (error: unknown) => {
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
console.error(error.message);
|
|
68
|
+
}
|
|
69
|
+
return {};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export class AmpGlobalEntityServiceImpl<WriteT extends BaseUpsertDto, ReadT extends BaseDto>
|
|
73
|
+
extends AmpEntityServiceImpl<WriteT, ReadT>
|
|
74
|
+
implements AmpGlobalEntityService<WriteT, ReadT>
|
|
75
|
+
{
|
|
76
|
+
constructor(rest: RestClient, kind: string, targetApi: TargetApi = TARGET_API_AGENT) {
|
|
77
|
+
super(rest, kind, targetApi);
|
|
78
|
+
}
|
|
79
|
+
getLookupIds(tid: number): Promise<ExtKeyMap> {
|
|
80
|
+
const req: RestRequest = {
|
|
81
|
+
url: `/${this.targetApi}/v1/${this.kind}/ext_key_map`,
|
|
82
|
+
method: 'GET',
|
|
83
|
+
params: {tid},
|
|
84
|
+
};
|
|
85
|
+
return this.call(req, extIdMapErrorHandler);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export class AmpSaaSEntityServiceImpl<WriteT extends BaseUpsertDto, ReadT extends BaseDto>
|
|
90
|
+
extends AmpEntityServiceImpl<WriteT, ReadT>
|
|
91
|
+
implements AmpSaaSEntityService<WriteT, ReadT>
|
|
92
|
+
{
|
|
93
|
+
constructor(rest: RestClient, kind: string, targetApi: TargetApi = TARGET_API_AGENT) {
|
|
94
|
+
super(rest, kind, targetApi);
|
|
95
|
+
}
|
|
96
|
+
getLookupIds(cid: number): Promise<ExtKeyMap> {
|
|
97
|
+
const req: RestRequest = {
|
|
98
|
+
url: `/${this.targetApi}/v1/${this.kind}/ext_key_map`,
|
|
99
|
+
method: 'GET',
|
|
100
|
+
params: {cid},
|
|
101
|
+
};
|
|
102
|
+
return this.call(req, extIdMapErrorHandler);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import axios, {Axios, AxiosError, AxiosStatic} from 'axios';
|
|
3
|
+
import {RestRequest} from './RestRequest';
|
|
4
|
+
import {RestResponse} from './RestResponse';
|
|
5
|
+
import {RestClientRetryStrategy, noopRestRetryStrategy} from './retry.rest';
|
|
6
|
+
import {HeadersMap, convertHeaders} from './utils';
|
|
7
|
+
import {RestClientRateLimitStrategy, StaticRestClientRateLimitStrategy, noopRestClientRateLimitStrategy} from './rateLimit.rest';
|
|
8
|
+
|
|
9
|
+
export type AmpRestClientOptions = {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
token: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
retryStrategy?: RestClientRetryStrategy;
|
|
14
|
+
rateLimitStrategy?: RestClientRateLimitStrategy;
|
|
15
|
+
verbose?: boolean;
|
|
16
|
+
client?: AxiosStatic;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type RestClientOptions = {
|
|
20
|
+
baseUrl?: string;
|
|
21
|
+
headers?: HeadersMap;
|
|
22
|
+
timeout?: number;
|
|
23
|
+
retryStrategy?: RestClientRetryStrategy;
|
|
24
|
+
rateLimitStrategy?: RestClientRateLimitStrategy;
|
|
25
|
+
verbose?: boolean;
|
|
26
|
+
client?: AxiosStatic;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* RestClient is a generic interface for making REST calls.
|
|
31
|
+
*/
|
|
32
|
+
export interface RestClient {
|
|
33
|
+
call(_request: RestRequest): Promise<RestResponse>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* DefaultRestClient is a default implementation of RestClient.
|
|
38
|
+
* It defaults to useing axios to make REST calls and adds reasonble defaults
|
|
39
|
+
* for retry logic, error handling, headers, and timeouts.
|
|
40
|
+
*
|
|
41
|
+
* These strategies can be useful when scraping 3rd party APIs.
|
|
42
|
+
*/
|
|
43
|
+
export class DefaultRestClient {
|
|
44
|
+
protected readonly ampAxios: Axios;
|
|
45
|
+
protected readonly retryStrategy: RestClientRetryStrategy;
|
|
46
|
+
protected readonly rateLimitStrategy: RestClientRateLimitStrategy;
|
|
47
|
+
protected readonly verbose: boolean;
|
|
48
|
+
constructor(options: RestClientOptions = {}) {
|
|
49
|
+
// TODO: rest client
|
|
50
|
+
const client = options.client ?? axios;
|
|
51
|
+
this.ampAxios = client.create({
|
|
52
|
+
baseURL: options.baseUrl,
|
|
53
|
+
timeout: options.timeout,
|
|
54
|
+
});
|
|
55
|
+
Object.keys(options.headers ?? {}).forEach(key => {
|
|
56
|
+
this.ampAxios.defaults.headers.common[key] = options.headers?.[key];
|
|
57
|
+
});
|
|
58
|
+
this.verbose = options.verbose ?? false;
|
|
59
|
+
this.ampAxios.defaults.headers.common['Accepts'] = 'application/json';
|
|
60
|
+
this.ampAxios.defaults.headers.post['Content-Type'] = 'application/json';
|
|
61
|
+
this.ampAxios.defaults.headers.put['Content-Type'] = 'application/json';
|
|
62
|
+
this.retryStrategy = options.retryStrategy ?? noopRestRetryStrategy;
|
|
63
|
+
this.rateLimitStrategy = options.rateLimitStrategy ?? noopRestClientRateLimitStrategy;
|
|
64
|
+
}
|
|
65
|
+
async call(request: RestRequest): Promise<RestResponse> {
|
|
66
|
+
const req = _.merge(request);
|
|
67
|
+
try {
|
|
68
|
+
await this.rateLimitStrategy.reserve(req);
|
|
69
|
+
if (this.verbose) console.log('Request', req);
|
|
70
|
+
const res = await this.ampAxios.request(req);
|
|
71
|
+
return {
|
|
72
|
+
status: res.status,
|
|
73
|
+
headers: convertHeaders(res),
|
|
74
|
+
data: res.data,
|
|
75
|
+
error: null,
|
|
76
|
+
};
|
|
77
|
+
} catch (err: unknown) {
|
|
78
|
+
if (err instanceof AxiosError) {
|
|
79
|
+
const axiosError = err as AxiosError;
|
|
80
|
+
return {
|
|
81
|
+
status: axiosError.response?.status ?? 500,
|
|
82
|
+
headers: axiosError.response ? convertHeaders(axiosError.response) : {},
|
|
83
|
+
data: null,
|
|
84
|
+
error: err,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const getAmpRestClient = (options: AmpRestClientOptions): RestClient => {
|
|
93
|
+
const ampClient = new DefaultRestClient({
|
|
94
|
+
baseUrl: options.baseUrl,
|
|
95
|
+
headers: {
|
|
96
|
+
Authorization: `Bearer ${options.token}`,
|
|
97
|
+
},
|
|
98
|
+
rateLimitStrategy: new StaticRestClientRateLimitStrategy(50),
|
|
99
|
+
verbose: options.verbose ?? false,
|
|
100
|
+
client: options.client,
|
|
101
|
+
});
|
|
102
|
+
return ampClient;
|
|
103
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {RestRequest} from './RestRequest';
|
|
2
|
+
|
|
3
|
+
export interface RestClientRateLimitStrategy {
|
|
4
|
+
reserve(_req: RestRequest): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const noopRestClientRateLimitStrategy: RestClientRateLimitStrategy = {
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
9
|
+
reserve(_req: RestRequest): Promise<void> {
|
|
10
|
+
return Promise.resolve();
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
15
|
+
|
|
16
|
+
export class StaticRestClientRateLimitStrategy implements RestClientRateLimitStrategy {
|
|
17
|
+
private readonly delay: number;
|
|
18
|
+
private lastRequest: number;
|
|
19
|
+
|
|
20
|
+
constructor(delay: number) {
|
|
21
|
+
this.delay = delay;
|
|
22
|
+
this.lastRequest = 0;
|
|
23
|
+
}
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
+
async reserve(_req: RestRequest): Promise<void> {
|
|
26
|
+
while (Date.now() - this.lastRequest < this.delay) {
|
|
27
|
+
await sleep(this.delay);
|
|
28
|
+
}
|
|
29
|
+
this.lastRequest = Date.now();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {AxiosError} from 'axios';
|
|
2
|
+
import {convertHeaders} from './utils';
|
|
3
|
+
import {RestRequest} from './RestRequest';
|
|
4
|
+
import {RestResponse} from './RestResponse';
|
|
5
|
+
|
|
6
|
+
export interface RestClientRetryStrategy {
|
|
7
|
+
onError(_req: RestRequest, _err: AxiosError): Promise<RestResponse>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const noopRestRetryStrategy: RestClientRetryStrategy = {
|
|
11
|
+
onError(req: RestRequest, err: AxiosError): Promise<RestResponse> {
|
|
12
|
+
const axiosError = err as AxiosError;
|
|
13
|
+
const res: RestResponse = {
|
|
14
|
+
status: axiosError.response?.status ?? 500,
|
|
15
|
+
headers: axiosError.response ? convertHeaders(axiosError.response) : {},
|
|
16
|
+
data: null,
|
|
17
|
+
error: err,
|
|
18
|
+
};
|
|
19
|
+
return Promise.resolve(res);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {AxiosResponse} from 'axios';
|
|
2
|
+
|
|
3
|
+
export type StringMap<T> = {[key: string]: T};
|
|
4
|
+
export type HeadersMap = StringMap<string>;
|
|
5
|
+
export type QueryMap = StringMap<unknown>;
|
|
6
|
+
|
|
7
|
+
export function convertHeaders(res: AxiosResponse): HeadersMap {
|
|
8
|
+
const headers: HeadersMap = {};
|
|
9
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
10
|
+
headers[key] = value as string;
|
|
11
|
+
}
|
|
12
|
+
return headers;
|
|
13
|
+
}
|