Files
floci-ui/packages/api/src/service/CloudProxyService.ts
T
Zakaria 6586f48539
CI / checks (pull_request) Successful in 26m22s
feat(api): add CloudFront distributions to the cloud explorer
2026-10-24 14:10:16 -04:00

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.`
}