feat(api): add AppSync GraphQL APIs to the cloud explorer
CI / checks (pull_request) Successful in 28m38s
CI / checks (pull_request) Successful in 28m38s
Create GraphQL APIs with API-key (the first key is made for you), IAM or Cognito user pool authorization, then edit the schema (a starter schema is offered, and a rejected one is explained), create and delete API keys, create data sources for a DynamoDB table, a Lambda function or none, and attach JavaScript resolvers to fields of the schema from a template for the data source. TheCloud's endpoint checks queries against the schema and the API key but does not run resolvers yet, so the page says so and offers no query runner.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import type {CloudProvider, FieldSchema, ServiceSchema, TableColumnSchema} from './types'
|
||||
|
||||
/** CreateGraphqlApi's name characters; the console keeps names to a readable length. */
|
||||
export const GRAPHQL_API_NAME_PATTERN = '^[A-Za-z0-9_\\- ]{1,128}$'
|
||||
/** CreateDataSource's name rule: a GraphQL-style identifier. */
|
||||
export const GRAPHQL_DATA_SOURCE_NAME_PATTERN = '^[_A-Za-z][_0-9A-Za-z]{0,64}$'
|
||||
export const GRAPHQL_AUTH_TYPES = ['API_KEY', 'AWS_IAM', 'AMAZON_COGNITO_USER_POOLS'] as const
|
||||
export const GRAPHQL_DATA_SOURCE_TYPES = ['NONE', 'AMAZON_DYNAMODB', 'AWS_LAMBDA'] as const
|
||||
/** AppSync's bounds on an API key's lifetime. */
|
||||
export const GRAPHQL_API_KEY_MAX_DAYS = 365
|
||||
|
||||
const apiColumns: TableColumnSchema[] = [
|
||||
{name: 'name', label: 'Name'},
|
||||
{name: 'id', label: 'API ID', path: 'metadata.apiId'},
|
||||
{name: 'auth', label: 'Authorization', path: 'metadata.authenticationLabel'},
|
||||
{name: 'endpoint', label: 'GraphQL endpoint', path: 'metadata.graphqlUrl', emptyText: '—'},
|
||||
]
|
||||
|
||||
const apiFilters: FieldSchema[] = [{name: 'search', label: 'Search', type: 'text', required: false}]
|
||||
|
||||
export function awsGraphqlSchema(): ServiceSchema {
|
||||
return {
|
||||
cloud: 'aws',
|
||||
service: 'graphql',
|
||||
displayName: 'AppSync GraphQL APIs',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'API name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
validation: {pattern: GRAPHQL_API_NAME_PATTERN, message: 'Use up to 128 letters, numbers, spaces, dashes and underscores'},
|
||||
},
|
||||
{
|
||||
name: 'authenticationType',
|
||||
label: 'Authorization',
|
||||
type: 'select',
|
||||
required: false,
|
||||
defaultValue: 'API_KEY',
|
||||
description: 'How clients prove who they are. With an API key, AppSync creates the first key for you.',
|
||||
options: [
|
||||
{label: 'API key', value: 'API_KEY'},
|
||||
{label: 'IAM (signed requests)', value: 'AWS_IAM'},
|
||||
{label: 'Cognito user pool', value: 'AMAZON_COGNITO_USER_POOLS'},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'userPoolId',
|
||||
label: 'User pool ID',
|
||||
type: 'text',
|
||||
required: false,
|
||||
requiredWhen: {field: 'authenticationType', equals: 'AMAZON_COGNITO_USER_POOLS'},
|
||||
visibleWhen: {field: 'authenticationType', values: ['AMAZON_COGNITO_USER_POOLS']},
|
||||
description: 'The Cognito user pool whose ID tokens the API accepts, such as us-east-1_AbC123.',
|
||||
validation: {pattern: '^[\\w-]+_[0-9a-zA-Z]+$', message: 'Use a user pool ID such as us-east-1_AbC123'},
|
||||
},
|
||||
],
|
||||
actions: ['list', 'create', 'delete', 'inspect'],
|
||||
capabilities: {
|
||||
resourceActions: [
|
||||
{name: 'list', label: 'List GraphQL APIs', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'create', label: 'Create GraphQL API', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'delete', label: 'Delete GraphQL API', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'inspect', label: 'Inspect GraphQL API', enabled: true, status: 'available', runtimeRequired: true},
|
||||
],
|
||||
graphqlActions: [
|
||||
{name: 'updateSchema', label: 'Save schema', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'createApiKey', label: 'Create API key', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'deleteApiKey', label: 'Delete API key', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'createDataSource', label: 'Create data source', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'deleteDataSource', label: 'Delete data source', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'createResolver', label: 'Attach resolver', enabled: true, status: 'available', runtimeRequired: true},
|
||||
{name: 'deleteResolver', label: 'Detach resolver', enabled: true, status: 'available', runtimeRequired: true},
|
||||
],
|
||||
},
|
||||
filters: apiFilters,
|
||||
columns: apiColumns,
|
||||
}
|
||||
}
|
||||
|
||||
export function graphqlSchemaFor(cloud: CloudProvider): ServiceSchema | null {
|
||||
return cloud === 'aws' ? awsGraphqlSchema() : null
|
||||
}
|
||||
@@ -238,6 +238,13 @@ export const SERVICE_CATALOG = {
|
||||
},
|
||||
identity: {displayName: 'Identity', iconKey: 'iam', group: 'Security', order: 5},
|
||||
apigateway: {displayName: 'API Gateway', iconKey: 'apigateway', group: 'Integration', order: 10},
|
||||
graphql: {
|
||||
displayName: 'GraphQL APIs',
|
||||
displayNameByCloud: {aws: 'AppSync'},
|
||||
iconKey: 'graphql',
|
||||
group: 'Integration',
|
||||
order: 12,
|
||||
},
|
||||
email: {
|
||||
displayName: 'Email',
|
||||
displayNameByCloud: {aws: 'SES Mailbox'},
|
||||
|
||||
@@ -188,6 +188,7 @@ export type UserPoolActionName =
|
||||
export type DistributionActionName = 'setEnabled' | 'createInvalidation'
|
||||
export type AutoScalingActionName = 'setCapacity' | 'terminateInstance'
|
||||
export type CertificateActionName = 'download' | 'export' | 'revoke'
|
||||
export type GraphqlActionName = 'updateSchema' | 'createApiKey' | 'deleteApiKey' | 'createDataSource' | 'deleteDataSource' | 'createResolver' | 'deleteResolver'
|
||||
export type QueryActionName = 'run' | 'results' | 'stop' | 'history' | 'catalog'
|
||||
export type DataCatalogActionName = 'createTable' | 'deleteTable'
|
||||
export type FileSystemActionName = 'createMountTarget' | 'deleteMountTarget' | 'createAccessPoint' | 'deleteAccessPoint' | 'attachFunction' | 'detachFunction'
|
||||
@@ -255,6 +256,7 @@ export interface ServiceSchema {
|
||||
fileSystemActions?: CapabilitySchema<FileSystemActionName>[]
|
||||
dataCatalogActions?: CapabilitySchema<DataCatalogActionName>[]
|
||||
queryActions?: CapabilitySchema<QueryActionName>[]
|
||||
graphqlActions?: CapabilitySchema<GraphqlActionName>[]
|
||||
monitoringActions?: CapabilitySchema<MonitoringActionName>[]
|
||||
/** API Gateway v2 (HTTP and WebSocket APIs): what the routes, stages and security panel can do. */
|
||||
httpApiActions?: CapabilitySchema<HttpApiActionName>[]
|
||||
@@ -562,6 +564,32 @@ export interface AccessPointInput {
|
||||
permissions: string
|
||||
}
|
||||
|
||||
export interface GraphqlApiKeyInput {
|
||||
description?: string
|
||||
/** Days until the key expires: 1 to 365, 7 when left out, as in the AppSync console. */
|
||||
expiresInDays?: number
|
||||
}
|
||||
|
||||
export interface GraphqlDataSourceInput {
|
||||
name: string
|
||||
description?: string
|
||||
type: 'NONE' | 'AMAZON_DYNAMODB' | 'AWS_LAMBDA'
|
||||
/** AMAZON_DYNAMODB: the table the resolvers read and write. */
|
||||
tableName?: string
|
||||
/** AWS_LAMBDA: the function the resolvers invoke. */
|
||||
functionArn?: string
|
||||
/** The role AppSync assumes to reach the table or function; AWS requires one for both. */
|
||||
serviceRoleArn?: string
|
||||
}
|
||||
|
||||
export interface GraphqlResolverInput {
|
||||
typeName: string
|
||||
fieldName: string
|
||||
dataSourceName: string
|
||||
/** An APPSYNC_JS resolver: request() and response() exported from one module. */
|
||||
code: string
|
||||
}
|
||||
|
||||
export interface QueryInput {
|
||||
sql: string
|
||||
/** The Glue database unqualified table names resolve in. */
|
||||
@@ -1294,6 +1322,13 @@ export interface CloudServiceAdapter {
|
||||
/** With `replace`, the group launches a new instance in its place; otherwise desired capacity drops by one. */
|
||||
terminateAutoScalingInstance?(groupName: string, instanceId: string, replace: boolean): Promise<void>
|
||||
getCertificatePem?(certificateId: string): Promise<CertificatePem>
|
||||
updateGraphqlSchema?(apiId: string, definition: string): Promise<CloudResource>
|
||||
createGraphqlApiKey?(apiId: string, input: GraphqlApiKeyInput): Promise<CloudResource>
|
||||
deleteGraphqlApiKey?(apiId: string, keyId: string): Promise<CloudResource>
|
||||
createGraphqlDataSource?(apiId: string, input: GraphqlDataSourceInput): Promise<CloudResource>
|
||||
deleteGraphqlDataSource?(apiId: string, name: string): Promise<CloudResource>
|
||||
createGraphqlResolver?(apiId: string, input: GraphqlResolverInput): Promise<CloudResource>
|
||||
deleteGraphqlResolver?(apiId: string, typeName: string, fieldName: string): Promise<CloudResource>
|
||||
startQuery?(workGroup: string, input: QueryInput): Promise<QueryExecutionSummary>
|
||||
/** The query's state, and its first rows once it has succeeded. */
|
||||
getQueryResult?(workGroup: string, queryId: string): Promise<QueryResult>
|
||||
|
||||
Reference in New Issue
Block a user