1078 lines
58 KiB
TypeScript
1078 lines
58 KiB
TypeScript
import type {
|
|
CloudAvailability,
|
|
CloudDescriptor,
|
|
CloudProvider,
|
|
CloudResource,
|
|
CloudServiceAdapter,
|
|
CloudServiceDescriptor,
|
|
CloudServiceType,
|
|
CloudServiceStatus,
|
|
CloudStatus,
|
|
CosmosContainer,
|
|
CosmosItem,
|
|
CosmosQueryResult,
|
|
CreateDatabaseSnapshotInput,
|
|
CreateKubernetesFargateProfileInput,
|
|
CreateKubernetesNodegroupInput,
|
|
CreateResourceInput,
|
|
AppConfigConfigurationProfile,
|
|
AppConfigDeployment,
|
|
AppConfigDeploymentStrategy,
|
|
AppConfigEnvironment,
|
|
AppConfigHostedConfigurationVersion,
|
|
ApiGatewayLambdaRouteInput,
|
|
ApiGatewayLambdaRouteResult,
|
|
ApiGatewayTestRouteInput,
|
|
ApiGatewayTestRouteResult,
|
|
ApiGatewayMethodTestInput,
|
|
ApiGatewayMethodTestResult,
|
|
ApiGatewayRoutesResult,
|
|
ApiGatewayDeleteRouteInput,
|
|
ApiGatewayCorsInput,
|
|
ApiGatewayCorsResult,
|
|
S3LambdaTriggerInput,
|
|
S3LambdaTriggerOverview,
|
|
S3BucketWebsiteConfiguration,
|
|
EventBridgeLambdaTriggerInput,
|
|
EventBridgeLambdaTriggerOverview,
|
|
EventBridgeRuleInput,
|
|
EventBridgeRuleOverview,
|
|
EventBridgePutEventInput,
|
|
EventBridgePutEventResult,
|
|
CloudFormationChangeSet,
|
|
CloudFormationChangeSetInput,
|
|
SqsLambdaEventSourceMappingInput,
|
|
SqsLambdaEventSourceMappingOverview,
|
|
KinesisPutRecordInput,
|
|
KinesisRecord,
|
|
LambdaEventSourceMappingInput,
|
|
LambdaEventSourceMappingOverview,
|
|
SqsMessage,
|
|
SqsMoveTaskInput,
|
|
SqsMoveTaskResult,
|
|
SqsReceiveMessagesInput,
|
|
SqsSendMessageInput,
|
|
DatabaseSnapshot,
|
|
KubernetesFargateProfile,
|
|
KubernetesNodegroup,
|
|
LogsInsightsQueryInput,
|
|
LogsInsightsQueryResult,
|
|
LogsFilterInput,
|
|
KmsDecryptInput,
|
|
KmsDecryptResult,
|
|
KmsEncryptInput,
|
|
KmsEncryptResult,
|
|
PublishNotificationInput,
|
|
PublishNotificationResult,
|
|
SnsSubscriptionInput,
|
|
SnsSubscriptionOverview,
|
|
ResourceQuery,
|
|
ServerlessInvokeResult,
|
|
SqlConnectionInput,
|
|
SqlDatabase,
|
|
SqlQueryResult,
|
|
SqlTable,
|
|
RuntimeReachability,
|
|
ServiceSchema,
|
|
StorageObjectDownload,
|
|
StorageObjectList,
|
|
UpdateResourceInput,
|
|
CreateUserPoolGroupInput,
|
|
UserPoolGroup,
|
|
UserPoolSignInTestInput,
|
|
UserPoolSignInTestResult,
|
|
UserPoolClientSettings,
|
|
DistributionInvalidation,
|
|
UpdateUserPoolClientOAuthInput,
|
|
UserPoolUserAction,
|
|
} from '../cloud-spi/types'
|
|
import type {
|
|
ChildCollection,
|
|
ChildItem,
|
|
CollectionPage,
|
|
DocumentStoreAdapter,
|
|
ItemStoreAdapter,
|
|
ItemQuery,
|
|
PageQuery,
|
|
} from '../cloud-spi/childCollections'
|
|
import {NotSupportedError} from '../cloud-spi/errors'
|
|
import {CloudAdapterRegistry} from '../registry/CloudAdapterRegistry'
|
|
import type {StartWorkflowExecutionInput, WorkflowExecutionDetail, WorkflowExecutionHistoryPage, WorkflowExecutionSummary} from '../cloud-spi/workflowExecutions'
|
|
import type {CodeBuildBuild, StartCodeBuildInput} from '../cloud-spi/codebuild'
|
|
import type {PipelineExecutionSummary, PipelineState} from '../cloud-spi/codepipeline'
|
|
import type {CodeDeployDeployment, StartCodeDeployDeploymentInput} from '../cloud-spi/codedeploy'
|
|
import {SERVICE_CATALOG_ENTRIES, displayNameFor, routeFor} from '../cloud-spi/serviceCatalog'
|
|
import {toHttpError} from '../cloud-spi/errors'
|
|
import {mapAwsSdkError} from '../adapter-aws/awsErrors'
|
|
import {endpointFor, runtimeProbes, type RuntimeProbe} from './runtimeProbe'
|
|
|
|
/**
|
|
* Status probes are real network calls and the console polls them on a short
|
|
* interval, so results are memoized briefly. Without this, asking for
|
|
* per-service detail turns one page load into a probe per registered service.
|
|
*/
|
|
const STATUS_TTL_MS = 5_000
|
|
|
|
export class CloudProxyService {
|
|
private readonly runtimeCache = new Map<CloudProvider, {at: number; value: {runtime: RuntimeReachability; checkedAt: string; error: string | null}}>()
|
|
private readonly serviceStatusCache = new Map<string, {at: number; value: CloudServiceStatus}>()
|
|
private readonly probes: Record<CloudProvider, RuntimeProbe>
|
|
|
|
/**
|
|
* `probes` is injectable so tests can exercise status without reaching the
|
|
* network — the probes hit real runtime endpoints, which are present on a
|
|
* developer machine and absent in CI.
|
|
*/
|
|
constructor(
|
|
private readonly registry: CloudAdapterRegistry,
|
|
probes: Record<CloudProvider, RuntimeProbe> = runtimeProbes,
|
|
) {
|
|
this.probes = probes
|
|
}
|
|
|
|
clouds(): CloudDescriptor[] {
|
|
return [
|
|
{id: 'aws', displayName: 'AWS', availability: 'available'},
|
|
{id: 'azure', displayName: 'Azure', availability: 'available'},
|
|
{id: 'gcp', displayName: 'GCP', availability: 'available'},
|
|
]
|
|
}
|
|
|
|
/**
|
|
* Derived from the service catalog and the adapter registry — never from a
|
|
* hardcoded per-cloud list. Registering an adapter is therefore the only
|
|
* thing needed to make a service appear as available in the UI.
|
|
*/
|
|
services(cloud: CloudProvider): CloudServiceDescriptor[] {
|
|
return SERVICE_CATALOG_ENTRIES.map((entry) => {
|
|
const adapter = this.registry.get(cloud, entry.service)
|
|
const override = adapter?.descriptorOverride?.() ?? {}
|
|
const derived: CloudAvailability = entry.legacyAvailability?.[cloud]
|
|
?? (adapter ? 'available' : 'coming_soon')
|
|
const availability = override.availability ?? derived
|
|
const displayName = override.displayName ?? displayNameFor(entry, cloud)
|
|
|
|
return {
|
|
cloud,
|
|
service: entry.service,
|
|
displayName,
|
|
availability,
|
|
reason: override.reason ?? unavailableReason(availability, cloud, displayName),
|
|
route: routeFor(entry, cloud),
|
|
iconKey: entry.iconKey,
|
|
group: entry.group,
|
|
order: entry.order,
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Only a registered adapter can describe a service. Returning a static
|
|
* schema for an unregistered pair used to make the UI render a table that
|
|
* then failed on every request.
|
|
*/
|
|
schema(cloud: CloudProvider, service: CloudServiceType): ServiceSchema | null {
|
|
return this.registry.get(cloud, service)?.schema() ?? null
|
|
}
|
|
|
|
/**
|
|
* Cloud-level reachability, probed against the runtime itself rather than
|
|
* inferred from one adapter's list call.
|
|
*
|
|
* Per-service detail is opt-in: the sidebar polls this every few seconds, so
|
|
* fanning out across every service by default would hammer the runtime.
|
|
*/
|
|
async status(cloud: CloudProvider, options: {includeServices?: boolean} = {}): Promise<CloudStatus> {
|
|
const services = this.registry.servicesFor(cloud)
|
|
const cached = await this.probeRuntime(cloud)
|
|
|
|
const base: CloudStatus = {
|
|
cloud,
|
|
adapterRegistered: services.length > 0,
|
|
runtime: cached.runtime,
|
|
endpoint: endpointFor(cloud),
|
|
checkedAt: cached.checkedAt,
|
|
error: cached.error,
|
|
}
|
|
|
|
if (!options.includeServices) return base
|
|
|
|
return {
|
|
...base,
|
|
services: await Promise.all(services.map((service) => this.serviceStatus(cloud, service))),
|
|
}
|
|
}
|
|
|
|
/** Health of a single service, so the UI can gate on the service it is showing. */
|
|
async serviceStatus(cloud: CloudProvider, service: CloudServiceType): Promise<CloudServiceStatus> {
|
|
const cacheKey = `${cloud}:${service}`
|
|
const cached = this.serviceStatusCache.get(cacheKey)
|
|
if (cached && Date.now() - cached.at < STATUS_TTL_MS) return cached.value
|
|
|
|
const value = await this.measureServiceStatus(cloud, service)
|
|
this.serviceStatusCache.set(cacheKey, {at: Date.now(), value})
|
|
return value
|
|
}
|
|
|
|
private async measureServiceStatus(cloud: CloudProvider, service: CloudServiceType): Promise<CloudServiceStatus> {
|
|
const endpoint = endpointFor(cloud)
|
|
const adapter = this.registry.get(cloud, service)
|
|
const checkedAt = new Date().toISOString()
|
|
|
|
if (!adapter) {
|
|
return {
|
|
cloud, service, adapterRegistered: false, runtime: 'coming_soon',
|
|
endpoint, checkedAt, latencyMs: null, error: null, errorCode: null,
|
|
}
|
|
}
|
|
|
|
const startedAt = performance.now()
|
|
try {
|
|
// list() is a valid probe for every current adapter; health() overrides it.
|
|
if (adapter.health) await adapter.health()
|
|
else await adapter.list()
|
|
return {
|
|
cloud, service, adapterRegistered: true, runtime: 'reachable',
|
|
endpoint, checkedAt, latencyMs: Math.round(performance.now() - startedAt),
|
|
error: null, errorCode: null,
|
|
}
|
|
} catch (error) {
|
|
// The mapped code is what lets the UI distinguish "the runtime does not
|
|
// implement this" from "the runtime is down".
|
|
const {body} = toHttpError(error, mapAwsSdkError)
|
|
return {
|
|
cloud, service, adapterRegistered: true, runtime: 'unavailable',
|
|
endpoint, checkedAt, latencyMs: Math.round(performance.now() - startedAt),
|
|
error: body.detail ?? body.message, errorCode: body.code,
|
|
}
|
|
}
|
|
}
|
|
|
|
private async probeRuntime(cloud: CloudProvider): Promise<{runtime: RuntimeReachability; checkedAt: string; error: string | null}> {
|
|
const cached = this.runtimeCache.get(cloud)
|
|
if (cached && Date.now() - cached.at < STATUS_TTL_MS) return cached.value
|
|
|
|
const checkedAt = new Date().toISOString()
|
|
let value: {runtime: RuntimeReachability; checkedAt: string; error: string | null}
|
|
try {
|
|
await this.probes[cloud]()
|
|
value = {runtime: 'reachable', checkedAt, error: null}
|
|
} catch (error) {
|
|
value = {
|
|
runtime: 'unavailable',
|
|
checkedAt,
|
|
error: error instanceof Error ? error.message : 'Runtime check failed',
|
|
}
|
|
}
|
|
|
|
this.runtimeCache.set(cloud, {at: Date.now(), value})
|
|
return value
|
|
}
|
|
|
|
async listResources(cloud: CloudProvider, service: CloudServiceType, query: ResourceQuery): Promise<CloudResource[]> {
|
|
return this.requireAdapter(cloud, service).list(query)
|
|
}
|
|
|
|
async getResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise<CloudResource | null> {
|
|
return this.requireAdapter(cloud, service).get(id)
|
|
}
|
|
|
|
async createResource(cloud: CloudProvider, service: CloudServiceType, input: CreateResourceInput): Promise<CloudResource> {
|
|
return this.requireAdapter(cloud, service).create(input)
|
|
}
|
|
|
|
async createApiGatewayLambdaRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayLambdaRouteInput): Promise<ApiGatewayLambdaRouteResult> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.createApiGatewayLambdaRoute) {
|
|
throw new NotSupportedError(`API Gateway route creation is not supported for ${cloud}`)
|
|
}
|
|
return adapter.createApiGatewayLambdaRoute(apiId, input)
|
|
}
|
|
|
|
async testApiGatewayRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayTestRouteInput): Promise<ApiGatewayTestRouteResult> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.testApiGatewayRoute) {
|
|
throw new NotSupportedError(`API Gateway route testing is not supported for ${cloud}`)
|
|
}
|
|
return adapter.testApiGatewayRoute(apiId, input)
|
|
}
|
|
|
|
async testApiGatewayMethod(cloud: CloudProvider, apiId: string, input: ApiGatewayMethodTestInput): Promise<ApiGatewayMethodTestResult> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.testApiGatewayMethod) {
|
|
throw new NotSupportedError(`API Gateway method testing is not supported for ${cloud}`)
|
|
}
|
|
return adapter.testApiGatewayMethod(apiId, input)
|
|
}
|
|
|
|
async listApiGatewayRoutes(cloud: CloudProvider, apiId: string): Promise<ApiGatewayRoutesResult> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.listApiGatewayRoutes) {
|
|
throw new NotSupportedError(`API Gateway route discovery is not supported for ${cloud}`)
|
|
}
|
|
return adapter.listApiGatewayRoutes(apiId)
|
|
}
|
|
|
|
async deleteApiGatewayRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayDeleteRouteInput): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.deleteApiGatewayRoute) throw new NotSupportedError(`API Gateway route deletion is not supported for ${cloud}`)
|
|
await adapter.deleteApiGatewayRoute(apiId, input)
|
|
}
|
|
|
|
async enableApiGatewayCors(cloud: CloudProvider, apiId: string, input: ApiGatewayCorsInput): Promise<ApiGatewayCorsResult> {
|
|
const adapter = this.requireAdapter(cloud, 'apigateway')
|
|
if (!adapter.enableApiGatewayCors) throw new NotSupportedError(`API Gateway CORS is not supported for ${cloud}`)
|
|
return adapter.enableApiGatewayCors(apiId, input)
|
|
}
|
|
|
|
async createS3LambdaTrigger(cloud: CloudProvider, bucketName: string, input: S3LambdaTriggerInput): Promise<S3LambdaTriggerOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.createS3LambdaTrigger) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`)
|
|
return adapter.createS3LambdaTrigger(bucketName, input)
|
|
}
|
|
|
|
async listS3LambdaTriggers(cloud: CloudProvider, bucketName: string): Promise<S3LambdaTriggerOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.listS3LambdaTriggers) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`)
|
|
return adapter.listS3LambdaTriggers(bucketName)
|
|
}
|
|
|
|
async deleteS3LambdaTrigger(cloud: CloudProvider, bucketName: string, triggerId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.deleteS3LambdaTrigger) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`)
|
|
await adapter.deleteS3LambdaTrigger(bucketName, triggerId)
|
|
}
|
|
|
|
async getS3BucketWebsite(cloud: CloudProvider, bucketName: string): Promise<S3BucketWebsiteConfiguration | null> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.getS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`)
|
|
return adapter.getS3BucketWebsite(bucketName)
|
|
}
|
|
|
|
async putS3BucketWebsite(cloud: CloudProvider, bucketName: string, configuration: S3BucketWebsiteConfiguration): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.putS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`)
|
|
await adapter.putS3BucketWebsite(bucketName, configuration)
|
|
}
|
|
|
|
async deleteS3BucketWebsite(cloud: CloudProvider, bucketName: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'storage')
|
|
if (!adapter.deleteS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`)
|
|
await adapter.deleteS3BucketWebsite(bucketName)
|
|
}
|
|
|
|
async createEventBridgeLambdaTrigger(cloud: CloudProvider, input: EventBridgeLambdaTriggerInput): Promise<EventBridgeLambdaTriggerOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.createEventBridgeLambdaTrigger) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`)
|
|
return adapter.createEventBridgeLambdaTrigger(input)
|
|
}
|
|
|
|
async listEventBridgeLambdaTriggers(cloud: CloudProvider, functionName: string): Promise<EventBridgeLambdaTriggerOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.listEventBridgeLambdaTriggers) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`)
|
|
return adapter.listEventBridgeLambdaTriggers(functionName)
|
|
}
|
|
|
|
async deleteEventBridgeLambdaTrigger(cloud: CloudProvider, functionName: string, triggerId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.deleteEventBridgeLambdaTrigger) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`)
|
|
await adapter.deleteEventBridgeLambdaTrigger(functionName, triggerId)
|
|
}
|
|
|
|
async listEventBridgeRules(cloud: CloudProvider, eventBusId: string): Promise<EventBridgeRuleOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'events')
|
|
if (!adapter.listEventBridgeRules) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`)
|
|
return adapter.listEventBridgeRules(eventBusId)
|
|
}
|
|
|
|
async createEventBridgeRule(cloud: CloudProvider, eventBusId: string, input: EventBridgeRuleInput): Promise<EventBridgeRuleOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'events')
|
|
if (!adapter.createEventBridgeRule) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`)
|
|
return adapter.createEventBridgeRule(eventBusId, input)
|
|
}
|
|
|
|
async deleteEventBridgeRule(cloud: CloudProvider, eventBusId: string, ruleName: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'events')
|
|
if (!adapter.deleteEventBridgeRule) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`)
|
|
await adapter.deleteEventBridgeRule(eventBusId, ruleName)
|
|
}
|
|
|
|
async putEventBridgeEvent(cloud: CloudProvider, eventBusId: string, input: EventBridgePutEventInput): Promise<EventBridgePutEventResult> {
|
|
const adapter = this.requireAdapter(cloud, 'events')
|
|
if (!adapter.putEventBridgeEvent) throw new NotSupportedError(`Event publishing is not supported for ${cloud}`)
|
|
return adapter.putEventBridgeEvent(eventBusId, input)
|
|
}
|
|
|
|
async listCloudFormationChangeSets(cloud: CloudProvider, stackName: string): Promise<CloudFormationChangeSet[]> {
|
|
const adapter = this.requireAdapter(cloud, 'iac')
|
|
if (!adapter.listCloudFormationChangeSets) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`)
|
|
return adapter.listCloudFormationChangeSets(stackName)
|
|
}
|
|
|
|
async createCloudFormationChangeSet(cloud: CloudProvider, stackName: string, input: CloudFormationChangeSetInput): Promise<CloudFormationChangeSet> {
|
|
const adapter = this.requireAdapter(cloud, 'iac')
|
|
if (!adapter.createCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`)
|
|
return adapter.createCloudFormationChangeSet(stackName, input)
|
|
}
|
|
|
|
async executeCloudFormationChangeSet(cloud: CloudProvider, stackName: string, changeSetName: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'iac')
|
|
if (!adapter.executeCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`)
|
|
await adapter.executeCloudFormationChangeSet(stackName, changeSetName)
|
|
}
|
|
|
|
async deleteCloudFormationChangeSet(cloud: CloudProvider, stackName: string, changeSetName: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'iac')
|
|
if (!adapter.deleteCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`)
|
|
await adapter.deleteCloudFormationChangeSet(stackName, changeSetName)
|
|
}
|
|
|
|
async updateCloudFormationTerminationProtection(cloud: CloudProvider, stackName: string, enabled: boolean): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'iac')
|
|
if (!adapter.updateCloudFormationTerminationProtection) throw new NotSupportedError(`CloudFormation termination protection is not supported for ${cloud}`)
|
|
await adapter.updateCloudFormationTerminationProtection(stackName, enabled)
|
|
}
|
|
|
|
async createSqsLambdaEventSourceMapping(cloud: CloudProvider, input: SqsLambdaEventSourceMappingInput): Promise<SqsLambdaEventSourceMappingOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.createSqsLambdaEventSourceMapping) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`)
|
|
return adapter.createSqsLambdaEventSourceMapping(input)
|
|
}
|
|
|
|
async listSqsLambdaEventSourceMappings(cloud: CloudProvider, functionName: string): Promise<SqsLambdaEventSourceMappingOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.listSqsLambdaEventSourceMappings) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`)
|
|
return adapter.listSqsLambdaEventSourceMappings(functionName)
|
|
}
|
|
|
|
async deleteSqsLambdaEventSourceMapping(cloud: CloudProvider, functionName: string, mappingId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.deleteSqsLambdaEventSourceMapping) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`)
|
|
await adapter.deleteSqsLambdaEventSourceMapping(functionName, mappingId)
|
|
}
|
|
|
|
async putKinesisRecord(cloud: CloudProvider, streamName: string, input: KinesisPutRecordInput): Promise<{sequenceNumber: string; shardId: string}> {
|
|
const adapter = this.requireAdapter(cloud, 'kinesis')
|
|
if (!adapter.putKinesisRecord) throw new NotSupportedError(`Kinesis record production is not supported for ${cloud}`)
|
|
return adapter.putKinesisRecord(streamName, input)
|
|
}
|
|
|
|
async listKinesisRecords(cloud: CloudProvider, streamName: string): Promise<KinesisRecord[]> {
|
|
const adapter = this.requireAdapter(cloud, 'kinesis')
|
|
if (!adapter.listKinesisRecords) throw new NotSupportedError(`Kinesis record inspection is not supported for ${cloud}`)
|
|
return adapter.listKinesisRecords(streamName)
|
|
}
|
|
|
|
async createLambdaEventSourceMapping(cloud: CloudProvider, input: LambdaEventSourceMappingInput): Promise<LambdaEventSourceMappingOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.createLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`)
|
|
return adapter.createLambdaEventSourceMapping(input)
|
|
}
|
|
|
|
async listLambdaEventSourceMappings(cloud: CloudProvider, filter?: {functionName?: string; eventSourceArn?: string}): Promise<LambdaEventSourceMappingOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.listLambdaEventSourceMappings) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`)
|
|
return adapter.listLambdaEventSourceMappings(filter)
|
|
}
|
|
|
|
async updateLambdaEventSourceMapping(cloud: CloudProvider, mappingId: string, input: {batchSize?: number; enabled?: boolean}): Promise<LambdaEventSourceMappingOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.updateLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`)
|
|
return adapter.updateLambdaEventSourceMapping(mappingId, input)
|
|
}
|
|
|
|
async deleteLambdaEventSourceMapping(cloud: CloudProvider, mappingId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'serverless')
|
|
if (!adapter.deleteLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`)
|
|
await adapter.deleteLambdaEventSourceMapping(mappingId)
|
|
}
|
|
|
|
async sendSqsMessage(cloud: CloudProvider, queueId: string, input: SqsSendMessageInput): Promise<{messageId: string}> {
|
|
const adapter = this.requireAdapter(cloud, 'messaging')
|
|
if (!adapter.sendSqsMessage) throw new NotSupportedError(`SQS message sending is not supported for ${cloud}`)
|
|
return adapter.sendSqsMessage(queueId, input)
|
|
}
|
|
|
|
async receiveSqsMessages(cloud: CloudProvider, queueId: string, input: SqsReceiveMessagesInput): Promise<SqsMessage[]> {
|
|
const adapter = this.requireAdapter(cloud, 'messaging')
|
|
if (!adapter.receiveSqsMessages) throw new NotSupportedError(`SQS message receiving is not supported for ${cloud}`)
|
|
return adapter.receiveSqsMessages(queueId, input)
|
|
}
|
|
|
|
async deleteSqsMessage(cloud: CloudProvider, queueId: string, receiptHandle: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'messaging')
|
|
if (!adapter.deleteSqsMessage) throw new NotSupportedError(`SQS message deletion is not supported for ${cloud}`)
|
|
await adapter.deleteSqsMessage(queueId, receiptHandle)
|
|
}
|
|
|
|
async purgeSqsQueue(cloud: CloudProvider, queueId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'messaging')
|
|
if (!adapter.purgeSqsQueue) throw new NotSupportedError(`SQS queue purging is not supported for ${cloud}`)
|
|
await adapter.purgeSqsQueue(queueId)
|
|
}
|
|
|
|
async startSqsMoveTask(cloud: CloudProvider, queueId: string, input: SqsMoveTaskInput): Promise<SqsMoveTaskResult> {
|
|
const adapter = this.requireAdapter(cloud, 'messaging')
|
|
if (!adapter.startSqsMoveTask) throw new NotSupportedError(`SQS message moving is not supported for ${cloud}`)
|
|
return adapter.startSqsMoveTask(queueId, input)
|
|
}
|
|
|
|
async updateResource(cloud: CloudProvider, service: CloudServiceType, id: string, input: UpdateResourceInput): Promise<CloudResource> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.update) throw new NotSupportedError(`Resource updates are not supported for ${cloud}/${service}`)
|
|
return adapter.update(id, input)
|
|
}
|
|
|
|
async dependencies(cloud: CloudProvider, service: CloudServiceType): Promise<Record<string, unknown>> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.dependencies) throw new NotSupportedError(`Form dependencies are not supported for ${cloud}/${service}`)
|
|
return adapter.dependencies()
|
|
}
|
|
|
|
async deleteResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise<void> {
|
|
await this.requireAdapter(cloud, service).delete(id)
|
|
}
|
|
|
|
async startResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.start) throw new NotSupportedError(`Resource start is not supported for ${cloud}/${service}`)
|
|
await adapter.start(id)
|
|
}
|
|
|
|
async stopResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.stop) throw new NotSupportedError(`Resource stop is not supported for ${cloud}/${service}`)
|
|
await adapter.stop(id)
|
|
}
|
|
|
|
async rebootResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.reboot) throw new NotSupportedError(`Resource reboot is not supported for ${cloud}/${service}`)
|
|
await adapter.reboot(id)
|
|
}
|
|
|
|
async encryptKms(cloud: CloudProvider, keyId: string, input: KmsEncryptInput): Promise<KmsEncryptResult> {
|
|
const adapter = this.requireAdapter(cloud, 'kms')
|
|
if (!adapter.encrypt) throw new NotSupportedError(`${cloud}/kms encrypt is not supported`)
|
|
return adapter.encrypt(keyId, input)
|
|
}
|
|
|
|
async decryptKms(cloud: CloudProvider, keyId: string, input: KmsDecryptInput): Promise<KmsDecryptResult> {
|
|
const adapter = this.requireAdapter(cloud, 'kms')
|
|
if (!adapter.decrypt) throw new NotSupportedError(`${cloud}/kms decrypt is not supported`)
|
|
return adapter.decrypt(keyId, input)
|
|
}
|
|
|
|
async invokeResource(
|
|
cloud: CloudProvider,
|
|
service: CloudServiceType,
|
|
id: string,
|
|
payload: string,
|
|
): Promise<ServerlessInvokeResult> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.invoke) throw new NotSupportedError(`${cloud}/${service} invoke is not supported`)
|
|
return adapter.invoke(id, payload)
|
|
}
|
|
|
|
async publishNotification(cloud: CloudProvider, topicArn: string, input: PublishNotificationInput): Promise<PublishNotificationResult> {
|
|
const adapter = this.requireAdapter(cloud, 'notifications')
|
|
if (!adapter.publish) throw new NotSupportedError(`Publishing notifications is not supported for ${cloud}`)
|
|
return adapter.publish(topicArn, input)
|
|
}
|
|
async createSnsSubscription(cloud: CloudProvider, topicArn: string, input: SnsSubscriptionInput): Promise<SnsSubscriptionOverview> {
|
|
const adapter = this.requireAdapter(cloud, 'notifications')
|
|
if (!adapter.createSnsSubscription) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`)
|
|
return adapter.createSnsSubscription(topicArn, input)
|
|
}
|
|
|
|
async listSnsSubscriptions(cloud: CloudProvider, topicArn: string): Promise<SnsSubscriptionOverview[]> {
|
|
const adapter = this.requireAdapter(cloud, 'notifications')
|
|
if (!adapter.listSnsSubscriptions) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`)
|
|
return adapter.listSnsSubscriptions(topicArn)
|
|
}
|
|
|
|
async deleteSnsSubscription(cloud: CloudProvider, subscriptionArn: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'notifications')
|
|
if (!adapter.deleteSnsSubscription) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`)
|
|
await adapter.deleteSnsSubscription(subscriptionArn)
|
|
}
|
|
async listObjects(cloud: CloudProvider, service: CloudServiceType, resourceId: string, prefix?: string): Promise<StorageObjectList> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.listObjects) throw new NotSupportedError(`Object listing is not supported for ${cloud}/${service}`)
|
|
return adapter.listObjects(resourceId, prefix)
|
|
}
|
|
|
|
async putObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string, body: Uint8Array, contentType: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.putObject) throw new NotSupportedError(`Object upload is not supported for ${cloud}/${service}`)
|
|
await adapter.putObject(resourceId, key, body, contentType)
|
|
}
|
|
|
|
async getObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string): Promise<StorageObjectDownload> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.getObject) throw new NotSupportedError(`Object download is not supported for ${cloud}/${service}`)
|
|
return adapter.getObject(resourceId, key)
|
|
}
|
|
|
|
async deleteObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.deleteObject) throw new NotSupportedError(`Object delete is not supported for ${cloud}/${service}`)
|
|
await adapter.deleteObject(resourceId, key)
|
|
}
|
|
|
|
async copyObject(cloud: CloudProvider, service: CloudServiceType, srcResourceId: string, srcKey: string, destKey: string, destResourceId?: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.copyObject) throw new NotSupportedError(`Object copy is not supported for ${cloud}/${service}`)
|
|
await adapter.copyObject(srcResourceId, srcKey, destKey, destResourceId)
|
|
}
|
|
|
|
// Child collections. `requireDocuments`/`requireItems` reject the wrong
|
|
// shape rather than the missing method, so a flat store asked for
|
|
// collections gets a 501 that names the actual problem.
|
|
|
|
async listChildCollections(cloud: CloudProvider, service: CloudServiceType, resourceId: string, page: PageQuery): Promise<CollectionPage<ChildCollection>> {
|
|
return this.requireDocuments(cloud, service).listCollections(resourceId, page)
|
|
}
|
|
|
|
async createChildCollection(cloud: CloudProvider, service: CloudServiceType, resourceId: string, input: CreateResourceInput): Promise<ChildCollection> {
|
|
const documents = this.requireDocuments(cloud, service)
|
|
if (!documents.createCollection) throw new NotSupportedError(`Creating collections is not supported for ${cloud}/${service}`)
|
|
return documents.createCollection(resourceId, input)
|
|
}
|
|
|
|
async deleteChildCollection(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string): Promise<void> {
|
|
const documents = this.requireDocuments(cloud, service)
|
|
if (!documents.deleteCollection) throw new NotSupportedError(`Deleting collections is not supported for ${cloud}/${service}`)
|
|
await documents.deleteCollection(resourceId, collectionId)
|
|
}
|
|
|
|
async listCollectionItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, page: PageQuery): Promise<CollectionPage<ChildItem>> {
|
|
return this.requireDocuments(cloud, service).listItems(resourceId, collectionId, page)
|
|
}
|
|
|
|
async putCollectionItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, body: Record<string, unknown>): Promise<ChildItem> {
|
|
const documents = this.requireDocuments(cloud, service)
|
|
if (!documents.putItem) throw new NotSupportedError(`Writing items is not supported for ${cloud}/${service}`)
|
|
return documents.putItem(resourceId, collectionId, body)
|
|
}
|
|
|
|
async deleteCollectionItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, itemId: string, partitionKey?: string | null): Promise<void> {
|
|
const documents = this.requireDocuments(cloud, service)
|
|
if (!documents.deleteItem) throw new NotSupportedError(`Deleting items is not supported for ${cloud}/${service}`)
|
|
await documents.deleteItem(resourceId, collectionId, itemId, partitionKey)
|
|
}
|
|
|
|
async queryCollectionItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, query: ItemQuery, page: PageQuery): Promise<CollectionPage<ChildItem>> {
|
|
const documents = this.requireDocuments(cloud, service)
|
|
if (!documents.queryItems) throw new NotSupportedError(`Querying items is not supported for ${cloud}/${service}`)
|
|
return documents.queryItems(resourceId, collectionId, query, page)
|
|
}
|
|
|
|
async listFlatItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, page: PageQuery): Promise<CollectionPage<ChildItem>> {
|
|
return this.requireItems(cloud, service).listItems(resourceId, page)
|
|
}
|
|
|
|
async getFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, itemId: string): Promise<ChildItem | null> {
|
|
const items = this.requireItems(cloud, service)
|
|
if (!items.getItem) throw new NotSupportedError(`Reading one item is not supported for ${cloud}/${service}`)
|
|
return items.getItem(resourceId, itemId)
|
|
}
|
|
|
|
async putFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, body: Record<string, unknown>): Promise<ChildItem> {
|
|
const items = this.requireItems(cloud, service)
|
|
if (!items.putItem) throw new NotSupportedError(`Writing items is not supported for ${cloud}/${service}`)
|
|
return items.putItem(resourceId, body)
|
|
}
|
|
|
|
async deleteFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, itemId: string, partitionKey?: string | null): Promise<void> {
|
|
const items = this.requireItems(cloud, service)
|
|
if (!items.deleteItem) throw new NotSupportedError(`Deleting items is not supported for ${cloud}/${service}`)
|
|
await items.deleteItem(resourceId, itemId, partitionKey)
|
|
}
|
|
|
|
async queryFlatItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, query: ItemQuery, page: PageQuery): Promise<CollectionPage<ChildItem>> {
|
|
const items = this.requireItems(cloud, service)
|
|
if (!items.queryItems) throw new NotSupportedError(`Querying items is not supported for ${cloud}/${service}`)
|
|
return items.queryItems(resourceId, query, page)
|
|
}
|
|
|
|
private requireDocuments(cloud: CloudProvider, service: CloudServiceType): DocumentStoreAdapter {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.documents) throw new NotSupportedError(`Nested collections are not supported for ${cloud}/${service}`)
|
|
return adapter.documents
|
|
}
|
|
|
|
private requireItems(cloud: CloudProvider, service: CloudServiceType): ItemStoreAdapter {
|
|
const adapter = this.requireAdapter(cloud, service)
|
|
if (!adapter.items) throw new NotSupportedError(`Flat items are not supported for ${cloud}/${service}`)
|
|
return adapter.items
|
|
}
|
|
|
|
async listCosmosContainers(cloud: CloudProvider, databaseId: string): Promise<CosmosContainer[]> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.listCosmosContainers) throw new NotSupportedError(`Cosmos containers are not supported for ${cloud}/database`)
|
|
return adapter.listCosmosContainers(databaseId)
|
|
}
|
|
|
|
async createCosmosContainer(cloud: CloudProvider, databaseId: string, input: CreateResourceInput): Promise<CosmosContainer> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.createCosmosContainer) throw new NotSupportedError(`Cosmos container creation is not supported for ${cloud}/database`)
|
|
return adapter.createCosmosContainer(databaseId, input)
|
|
}
|
|
|
|
async deleteCosmosContainer(cloud: CloudProvider, databaseId: string, containerId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.deleteCosmosContainer) throw new NotSupportedError(`Cosmos container deletion is not supported for ${cloud}/database`)
|
|
await adapter.deleteCosmosContainer(databaseId, containerId)
|
|
}
|
|
|
|
async listCosmosItems(cloud: CloudProvider, databaseId: string, containerId: string): Promise<CosmosItem[]> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.listCosmosItems) throw new NotSupportedError(`Cosmos items are not supported for ${cloud}/database`)
|
|
return adapter.listCosmosItems(databaseId, containerId)
|
|
}
|
|
|
|
async upsertCosmosItem(cloud: CloudProvider, databaseId: string, containerId: string, document: Record<string, unknown>): Promise<CosmosItem> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.upsertCosmosItem) throw new NotSupportedError(`Cosmos item upsert is not supported for ${cloud}/database`)
|
|
return adapter.upsertCosmosItem(databaseId, containerId, document)
|
|
}
|
|
|
|
async deleteCosmosItem(cloud: CloudProvider, databaseId: string, containerId: string, itemId: string, partitionKey?: string | null): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.deleteCosmosItem) throw new NotSupportedError(`Cosmos item deletion is not supported for ${cloud}/database`)
|
|
await adapter.deleteCosmosItem(databaseId, containerId, itemId, partitionKey)
|
|
}
|
|
|
|
async queryCosmosItems(cloud: CloudProvider, databaseId: string, containerId: string, query: string): Promise<CosmosQueryResult> {
|
|
const adapter = this.requireAdapter(cloud, 'nosql')
|
|
if (!adapter.queryCosmosItems) throw new NotSupportedError(`Cosmos query is not supported for ${cloud}/database`)
|
|
return adapter.queryCosmosItems(databaseId, containerId, query)
|
|
}
|
|
|
|
async listDatabaseSnapshots(cloud: CloudProvider, instanceIdentifier?: string): Promise<DatabaseSnapshot[]> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.listDatabaseSnapshots) throw new NotSupportedError(`Snapshot listing is not supported for ${cloud}/database`)
|
|
return adapter.listDatabaseSnapshots(instanceIdentifier)
|
|
}
|
|
|
|
async createDatabaseSnapshot(cloud: CloudProvider, input: CreateDatabaseSnapshotInput): Promise<DatabaseSnapshot> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.createDatabaseSnapshot) throw new NotSupportedError(`Snapshot creation is not supported for ${cloud}/database`)
|
|
return adapter.createDatabaseSnapshot(input)
|
|
}
|
|
|
|
async listDatabaseOrderableInstanceClasses(cloud: CloudProvider, engine?: string): Promise<string[]> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.listDatabaseOrderableInstanceClasses) throw new NotSupportedError(`Orderable instance class listing is not supported for ${cloud}/database`)
|
|
return adapter.listDatabaseOrderableInstanceClasses(engine)
|
|
}
|
|
|
|
async listSqlDatabases(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput): Promise<SqlDatabase[]> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.listSqlDatabases) throw new NotSupportedError(`SQL database browsing is not supported for ${cloud}/database`)
|
|
return adapter.listSqlDatabases(serverId, connection)
|
|
}
|
|
|
|
async listSqlTables(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput): Promise<SqlTable[]> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.listSqlTables) throw new NotSupportedError(`SQL table browsing is not supported for ${cloud}/database`)
|
|
return adapter.listSqlTables(serverId, connection)
|
|
}
|
|
|
|
async querySql(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput, query: string): Promise<SqlQueryResult> {
|
|
const adapter = this.requireAdapter(cloud, 'database')
|
|
if (!adapter.querySql) throw new NotSupportedError(`SQL query is not supported for ${cloud}/database`)
|
|
return adapter.querySql(serverId, connection, query)
|
|
}
|
|
|
|
async queryLogs(cloud: CloudProvider, logGroupName: string, input: LogsInsightsQueryInput): Promise<LogsInsightsQueryResult> {
|
|
const adapter = this.requireAdapter(cloud, 'logs')
|
|
if (!adapter.queryLogs) throw new NotSupportedError(`Logs Insights query is not supported for ${cloud}/logs`)
|
|
return adapter.queryLogs(logGroupName, input)
|
|
}
|
|
|
|
async filterLogEvents(cloud: CloudProvider, logGroupName: string, input: LogsFilterInput, page: PageQuery = {}): Promise<CollectionPage<ChildItem>> {
|
|
const adapter = this.requireAdapter(cloud, 'logs')
|
|
if (!adapter.filterLogEvents) throw new NotSupportedError(`Log event filtering is not supported for ${cloud}/logs`)
|
|
return adapter.filterLogEvents(logGroupName, input, page)
|
|
}
|
|
|
|
async updateUserPoolUser(cloud: CloudProvider, poolId: string, username: string, input: UserPoolUserAction): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.updateUserPoolUser) throw new NotSupportedError(`User actions are not supported for ${cloud}/userpools`)
|
|
await adapter.updateUserPoolUser(poolId, username, input)
|
|
}
|
|
|
|
async setUserPoolUserGroups(cloud: CloudProvider, poolId: string, username: string, groups: string[]): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.setUserPoolUserGroups) throw new NotSupportedError(`User groups are not supported for ${cloud}/userpools`)
|
|
await adapter.setUserPoolUserGroups(poolId, username, groups)
|
|
}
|
|
|
|
async listUserPoolGroups(cloud: CloudProvider, poolId: string): Promise<UserPoolGroup[]> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.listUserPoolGroups) throw new NotSupportedError(`Groups are not supported for ${cloud}/userpools`)
|
|
return adapter.listUserPoolGroups(poolId)
|
|
}
|
|
|
|
async createUserPoolGroup(cloud: CloudProvider, poolId: string, input: CreateUserPoolGroupInput): Promise<UserPoolGroup> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.createUserPoolGroup) throw new NotSupportedError(`Group creation is not supported for ${cloud}/userpools`)
|
|
return adapter.createUserPoolGroup(poolId, input)
|
|
}
|
|
|
|
async deleteUserPoolGroup(cloud: CloudProvider, poolId: string, groupName: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.deleteUserPoolGroup) throw new NotSupportedError(`Group deletion is not supported for ${cloud}/userpools`)
|
|
await adapter.deleteUserPoolGroup(poolId, groupName)
|
|
}
|
|
|
|
async testUserPoolSignIn(cloud: CloudProvider, poolId: string, input: UserPoolSignInTestInput): Promise<UserPoolSignInTestResult> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.testUserPoolSignIn) throw new NotSupportedError(`Test sign-in is not supported for ${cloud}/userpools`)
|
|
return adapter.testUserPoolSignIn(poolId, input)
|
|
}
|
|
|
|
async setDistributionEnabled(cloud: CloudProvider, distributionId: string, enabled: boolean): Promise<CloudResource> {
|
|
const adapter = this.requireAdapter(cloud, 'cdn')
|
|
if (!adapter.setDistributionEnabled) throw new NotSupportedError(`Enabling distributions is not supported for ${cloud}/cdn`)
|
|
return adapter.setDistributionEnabled(distributionId, enabled)
|
|
}
|
|
|
|
async createDistributionInvalidation(cloud: CloudProvider, distributionId: string, paths: string[]): Promise<DistributionInvalidation> {
|
|
const adapter = this.requireAdapter(cloud, 'cdn')
|
|
if (!adapter.createDistributionInvalidation) throw new NotSupportedError(`Invalidations are not supported for ${cloud}/cdn`)
|
|
return adapter.createDistributionInvalidation(distributionId, paths)
|
|
}
|
|
|
|
async describeUserPoolClient(cloud: CloudProvider, poolId: string, clientId: string): Promise<UserPoolClientSettings> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.describeUserPoolClient) throw new NotSupportedError(`App client settings are not supported for ${cloud}/userpools`)
|
|
return adapter.describeUserPoolClient(poolId, clientId)
|
|
}
|
|
|
|
async updateUserPoolClientOAuth(cloud: CloudProvider, poolId: string, clientId: string, input: UpdateUserPoolClientOAuthInput): Promise<UserPoolClientSettings> {
|
|
const adapter = this.requireAdapter(cloud, 'userpools')
|
|
if (!adapter.updateUserPoolClientOAuth) throw new NotSupportedError(`App client settings are not supported for ${cloud}/userpools`)
|
|
return adapter.updateUserPoolClientOAuth(poolId, clientId, input)
|
|
}
|
|
|
|
async listKubernetesNodegroups(cloud: CloudProvider, clusterId: string): Promise<KubernetesNodegroup[]> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.listKubernetesNodegroups) throw new NotSupportedError(`Nodegroups are not supported for ${cloud}/k8s`)
|
|
return adapter.listKubernetesNodegroups(clusterId)
|
|
}
|
|
|
|
async createKubernetesNodegroup(cloud: CloudProvider, clusterId: string, input: CreateKubernetesNodegroupInput): Promise<KubernetesNodegroup> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.createKubernetesNodegroup) throw new NotSupportedError(`Nodegroup creation is not supported for ${cloud}/k8s`)
|
|
return adapter.createKubernetesNodegroup(clusterId, input)
|
|
}
|
|
|
|
async deleteKubernetesNodegroup(cloud: CloudProvider, clusterId: string, nodegroupId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.deleteKubernetesNodegroup) throw new NotSupportedError(`Nodegroup deletion is not supported for ${cloud}/k8s`)
|
|
await adapter.deleteKubernetesNodegroup(clusterId, nodegroupId)
|
|
}
|
|
|
|
async listKubernetesFargateProfiles(cloud: CloudProvider, clusterId: string): Promise<KubernetesFargateProfile[]> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.listKubernetesFargateProfiles) throw new NotSupportedError(`Fargate profiles are not supported for ${cloud}/k8s`)
|
|
return adapter.listKubernetesFargateProfiles(clusterId)
|
|
}
|
|
|
|
async createKubernetesFargateProfile(cloud: CloudProvider, clusterId: string, input: CreateKubernetesFargateProfileInput): Promise<KubernetesFargateProfile> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.createKubernetesFargateProfile) throw new NotSupportedError(`Fargate profile creation is not supported for ${cloud}/k8s`)
|
|
return adapter.createKubernetesFargateProfile(clusterId, input)
|
|
}
|
|
|
|
async deleteKubernetesFargateProfile(cloud: CloudProvider, clusterId: string, profileId: string): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'k8s')
|
|
if (!adapter.deleteKubernetesFargateProfile) throw new NotSupportedError(`Fargate profile deletion is not supported for ${cloud}/k8s`)
|
|
await adapter.deleteKubernetesFargateProfile(clusterId, profileId)
|
|
}
|
|
|
|
async clearEmailInbox(cloud: CloudProvider): Promise<void> {
|
|
const adapter = this.requireAdapter(cloud, 'email')
|
|
if (!adapter.clearEmailInbox) throw new NotSupportedError(`Inbox clearing is not supported for ${cloud}/email`)
|
|
await adapter.clearEmailInbox()
|
|
}
|
|
|
|
private appConfigAdapter(cloud: CloudProvider): CloudServiceAdapter {
|
|
const adapter = this.requireAdapter(cloud, 'configuration')
|
|
const method = adapter.listAppConfigEnvironments
|
|
if (!method) throw new NotSupportedError(`AppConfig resources are not supported for ${cloud}/configuration`)
|
|
return adapter
|
|
}
|
|
|
|
async listAppConfigEnvironments(cloud: CloudProvider, applicationId: string): Promise<AppConfigEnvironment[]> {
|
|
return this.appConfigAdapter(cloud).listAppConfigEnvironments!(applicationId)
|
|
}
|
|
|
|
async createAppConfigEnvironment(cloud: CloudProvider, applicationId: string, input: CreateResourceInput): Promise<AppConfigEnvironment> {
|
|
return this.appConfigAdapter(cloud).createAppConfigEnvironment!(applicationId, input)
|
|
}
|
|
|
|
async deleteAppConfigEnvironment(cloud: CloudProvider, applicationId: string, environmentId: string): Promise<void> {
|
|
await this.appConfigAdapter(cloud).deleteAppConfigEnvironment!(applicationId, environmentId)
|
|
}
|
|
|
|
async listAppConfigConfigurationProfiles(cloud: CloudProvider, applicationId: string): Promise<AppConfigConfigurationProfile[]> {
|
|
return this.appConfigAdapter(cloud).listAppConfigConfigurationProfiles!(applicationId)
|
|
}
|
|
|
|
async createAppConfigConfigurationProfile(cloud: CloudProvider, applicationId: string, input: CreateResourceInput): Promise<AppConfigConfigurationProfile> {
|
|
return this.appConfigAdapter(cloud).createAppConfigConfigurationProfile!(applicationId, input)
|
|
}
|
|
|
|
async deleteAppConfigConfigurationProfile(cloud: CloudProvider, applicationId: string, profileId: string): Promise<void> {
|
|
await this.appConfigAdapter(cloud).deleteAppConfigConfigurationProfile!(applicationId, profileId)
|
|
}
|
|
|
|
async listAppConfigHostedConfigurationVersions(cloud: CloudProvider, applicationId: string, profileId: string): Promise<AppConfigHostedConfigurationVersion[]> {
|
|
return this.appConfigAdapter(cloud).listAppConfigHostedConfigurationVersions!(applicationId, profileId)
|
|
}
|
|
|
|
async getAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, versionNumber: number): Promise<AppConfigHostedConfigurationVersion | null> {
|
|
return this.appConfigAdapter(cloud).getAppConfigHostedConfigurationVersion!(applicationId, profileId, versionNumber)
|
|
}
|
|
|
|
async createAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, input: CreateResourceInput): Promise<AppConfigHostedConfigurationVersion> {
|
|
return this.appConfigAdapter(cloud).createAppConfigHostedConfigurationVersion!(applicationId, profileId, input)
|
|
}
|
|
|
|
async deleteAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, versionNumber: number): Promise<void> {
|
|
await this.appConfigAdapter(cloud).deleteAppConfigHostedConfigurationVersion!(applicationId, profileId, versionNumber)
|
|
}
|
|
|
|
async listAppConfigDeploymentStrategies(cloud: CloudProvider): Promise<AppConfigDeploymentStrategy[]> {
|
|
return this.appConfigAdapter(cloud).listAppConfigDeploymentStrategies!()
|
|
}
|
|
|
|
async createAppConfigDeploymentStrategy(cloud: CloudProvider, input: CreateResourceInput): Promise<AppConfigDeploymentStrategy> {
|
|
return this.appConfigAdapter(cloud).createAppConfigDeploymentStrategy!(input)
|
|
}
|
|
|
|
async deleteAppConfigDeploymentStrategy(cloud: CloudProvider, strategyId: string): Promise<void> {
|
|
await this.appConfigAdapter(cloud).deleteAppConfigDeploymentStrategy!(strategyId)
|
|
}
|
|
|
|
async startAppConfigDeployment(cloud: CloudProvider, applicationId: string, environmentId: string, input: CreateResourceInput): Promise<AppConfigDeployment> {
|
|
return this.appConfigAdapter(cloud).startAppConfigDeployment!(applicationId, environmentId, input)
|
|
}
|
|
|
|
async getAppConfigDeployment(cloud: CloudProvider, applicationId: string, environmentId: string, deploymentNumber: number): Promise<AppConfigDeployment | null> {
|
|
return this.appConfigAdapter(cloud).getAppConfigDeployment!(applicationId, environmentId, deploymentNumber)
|
|
}
|
|
|
|
async startWorkflowExecution(cloud: CloudProvider, stateMachineArn: string, input: StartWorkflowExecutionInput): Promise<{executionArn: string; startDate: Date}> {
|
|
return this.workflowAdapter(cloud).startExecution(stateMachineArn, input)
|
|
}
|
|
|
|
async listWorkflowExecutions(cloud: CloudProvider, stateMachineArn: string): Promise<WorkflowExecutionSummary[]> {
|
|
return this.workflowAdapter(cloud).listExecutions(stateMachineArn)
|
|
}
|
|
|
|
async describeWorkflowExecution(cloud: CloudProvider, executionArn: string): Promise<WorkflowExecutionDetail> {
|
|
return this.workflowAdapter(cloud).describeExecution(executionArn)
|
|
}
|
|
|
|
async getWorkflowExecutionHistory(cloud: CloudProvider, executionArn: string, nextToken?: string): Promise<WorkflowExecutionHistoryPage> {
|
|
return this.workflowAdapter(cloud).getExecutionHistory(executionArn, nextToken)
|
|
}
|
|
|
|
async stopWorkflowExecution(cloud: CloudProvider, executionArn: string): Promise<void> {
|
|
await this.workflowAdapter(cloud).stopExecution(executionArn)
|
|
}
|
|
|
|
async startCodeBuild(cloud: CloudProvider, projectName: string, input: StartCodeBuildInput): Promise<CodeBuildBuild> {
|
|
return this.codeBuildAdapter(cloud).startBuild!(projectName, input)
|
|
}
|
|
|
|
async listCodeBuilds(cloud: CloudProvider, projectName: string): Promise<CodeBuildBuild[]> {
|
|
return this.codeBuildAdapter(cloud).listBuilds!(projectName)
|
|
}
|
|
|
|
async getCodeBuild(cloud: CloudProvider, id: string): Promise<CodeBuildBuild | null> {
|
|
return this.codeBuildAdapter(cloud).getBuild!(id)
|
|
}
|
|
|
|
async startPipeline(cloud: CloudProvider, id: string): Promise<{id: string}> {
|
|
return this.codePipelineAdapter(cloud).startPipeline!(id)
|
|
}
|
|
|
|
async listPipelineExecutions(cloud: CloudProvider, id: string): Promise<PipelineExecutionSummary[]> {
|
|
return this.codePipelineAdapter(cloud).listPipelineExecutions!(id)
|
|
}
|
|
|
|
async getPipelineState(cloud: CloudProvider, id: string): Promise<PipelineState> {
|
|
return this.codePipelineAdapter(cloud).getPipelineState!(id)
|
|
}
|
|
|
|
async startCodeDeployDeployment(cloud: CloudProvider, applicationName: string, deploymentGroupName: string, input: StartCodeDeployDeploymentInput): Promise<CodeDeployDeployment> {
|
|
return this.codeDeployAdapter(cloud).startCodeDeployDeployment!(applicationName, deploymentGroupName, input)
|
|
}
|
|
|
|
async listCodeDeployDeployments(cloud: CloudProvider, applicationName: string, deploymentGroupName: string): Promise<CodeDeployDeployment[]> {
|
|
return this.codeDeployAdapter(cloud).listCodeDeployDeployments!(applicationName, deploymentGroupName)
|
|
}
|
|
|
|
async getCodeDeployDeployment(cloud: CloudProvider, id: string): Promise<CodeDeployDeployment> {
|
|
return this.codeDeployAdapter(cloud).getCodeDeployDeployment!(id)
|
|
}
|
|
|
|
async stopCodeDeployDeployment(cloud: CloudProvider, id: string, rollback: boolean): Promise<void> {
|
|
await this.codeDeployAdapter(cloud).stopCodeDeployDeployment!(id, rollback)
|
|
}
|
|
|
|
private workflowAdapter(cloud: CloudProvider) {
|
|
const adapter = this.requireAdapter(cloud, 'workflows')
|
|
if (!('startExecution' in adapter) || !('listExecutions' in adapter) || !('describeExecution' in adapter) || !('getExecutionHistory' in adapter) || !('stopExecution' in adapter)) {
|
|
throw new NotSupportedError(`Workflow executions are not supported for ${cloud}`)
|
|
}
|
|
return adapter as import('../adapter-aws/AwsStepFunctionsAdapter').AwsStepFunctionsAdapter
|
|
}
|
|
|
|
private codeBuildAdapter(cloud: CloudProvider) {
|
|
const adapter = this.requireAdapter(cloud, 'codebuild')
|
|
if (!adapter.startBuild || !adapter.listBuilds || !adapter.getBuild) {
|
|
throw new NotSupportedError(`CodeBuild builds are not supported for ${cloud}`)
|
|
}
|
|
return adapter
|
|
}
|
|
|
|
private codePipelineAdapter(cloud: CloudProvider) {
|
|
const adapter = this.requireAdapter(cloud, 'codepipeline')
|
|
if (!adapter.startPipeline || !adapter.listPipelineExecutions || !adapter.getPipelineState) {
|
|
throw new NotSupportedError(`CodePipeline executions are not supported for ${cloud}`)
|
|
}
|
|
return adapter
|
|
}
|
|
|
|
private codeDeployAdapter(cloud: CloudProvider) {
|
|
const adapter = this.requireAdapter(cloud, 'codedeploy')
|
|
if (!adapter.startCodeDeployDeployment || !adapter.listCodeDeployDeployments || !adapter.getCodeDeployDeployment || !adapter.stopCodeDeployDeployment) {
|
|
throw new NotSupportedError(`CodeDeploy deployments are not supported for ${cloud}`)
|
|
}
|
|
return adapter
|
|
}
|
|
|
|
private requireAdapter(cloud: CloudProvider, service: CloudServiceType) {
|
|
const adapter = this.registry.get(cloud, service)
|
|
if (!adapter) throw new NotSupportedError(`No adapter registered for ${cloud}/${service}`)
|
|
return adapter
|
|
}
|
|
}
|
|
|
|
/** Keeps the promise that every coming_soon descriptor explains itself. */
|
|
function unavailableReason(
|
|
availability: CloudAvailability,
|
|
cloud: CloudProvider,
|
|
displayName: string,
|
|
): string | undefined {
|
|
if (availability === 'available') return undefined
|
|
return `No ${cloud.toUpperCase()} adapter is registered for ${displayName} yet.`
|
|
}
|