Skip to content

Instantly share code, notes, and snippets.

@onlybakam
Created February 7, 2023 21:26
Show Gist options
  • Save onlybakam/fd68e5d0d192a7e0a2822d960067a6fe to your computer and use it in GitHub Desktop.
Save onlybakam/fd68e5d0d192a7e0a2822d960067a6fe to your computer and use it in GitHub Desktop.
import {
util,
DynamoDBGetItem,
DynamoDBExpression,
DynamoDBPutItemRequest,
DynamoDBUpdateItemRequest,
DynamoDBDeleteItemRequest,
LambdaRequest,
Context as _Context,
DynamoDBQueryRequest,
} from '@aws-appsync/utils'
export type ExpressionOperation<T> =
| { [k in 'contains' | 'notContains']?: T }
| { beginsWith: T }
| { attributeType: 'S' | 'SS' }
| { [k in 'eq' | 'ne' | 'gt' | 'ge' | 'lt' | 'le']?: T }
| { in?: T[] }
| { between?: [T, T] }
| { [k in 'attributeExists']: Boolean }
export type Expression<T> = {
[k in keyof T]?: ExpressionOperation<T[k]>
} & {
and?: Expression<T> | Expression<T>[]
or?: Expression<T> | Expression<T>[]
not?: Expression<T>
}
// type Todo = {
// id: string
// title: string
// upvotes?: number
// downvotes?: number
// percentageUp?: number
// value?: number
// isPublished?: boolean
// }
// const x: Expression<Todo> = {
// title: { contains: 'hihihi' },
// upvotes: { eq: 5 },
// downvotes: { eq: 1 },
// percentageUp: { in: [77.7, 88.8] },
// and: [
// { isPublished: { eq: true } },
// {
// percentageUp: { contains: 90.5 },
// upvotes: { gt: 10 },
// },
// ],
// or: [
// { isPublished: { eq: true } },
// {
// percentageUp: { contains: 10.5 },
// upvotes: { le: 5 },
// and: {
// downvotes: { ge: 10 },
// },
// },
// ],
// not: {
// title: { eq: 'Unpublished' },
// },
// }
// type DynamoDBExpression = {
// expression: string
// expressionNames: Record<string, string>
// expressionValues?: Record<string, any>
// }
export function getExpression<T, K = {}>(expression: Expression<T & K>) {
// if (!expression) return null
const filter = JSON.parse(util.transform.toDynamoDBFilterExpression(expression))
if (filter && filter.expressionValues && !Object.keys(filter.expressionValues).length) {
delete filter.expressionValues
}
return filter as DynamoDBExpression
}
export function get<T = {}, K = {}>(
key: Partial<T> & K,
otherParams?: Omit<DynamoDBGetItem, 'operation' | 'key'>
): DynamoDBGetItem {
return {
operation: 'GetItem',
key: util.dynamodb.toMapValues(key),
...otherParams,
}
}
export function put<T extends {}, K extends {} | undefined = undefined>(
key: K extends undefined ? Partial<T> : K & Partial<T>,
item: K extends undefined ? T : Omit<T, keyof K>,
otherParams?: Pick<
DynamoDBPutItemRequest,
'condition' | 'customPartitionKey' | 'populateIndexFields' | '_version'
>
): DynamoDBPutItemRequest {
return {
operation: 'PutItem',
key: util.dynamodb.toMapValues(key),
attributeValues: util.dynamodb.toMapValues(item),
...otherParams,
}
}
type TempType<T> = {
sets: string[]
removes: string[]
// names?: { [P in keyof T as `#${string & P}`]?: P }
// values?: { [P in keyof T as `:${string & P}`]?: T[P] }
names: Record<string, string>
values: Record<string, any>
}
export function update<T extends {}, K = {}>(
key: K & Partial<T>,
values: Partial<T>,
otherParams?: Pick<
DynamoDBUpdateItemRequest,
'condition' | 'customPartitionKey' | 'populateIndexFields' | '_version'
>
): DynamoDBUpdateItemRequest {
const exp = Object.entries(values).reduce(
(prev, [k, v]) => {
prev.names[`#${k}`] = k
if (v) {
const a = prev.sets
a.push(`#${k} = :${k}`)
// prev.sets.push(`#${k} = :${k}`)
prev.values[`:${k}`] = v
} else {
const a = prev.removes
a.push(`#${k}`)
// prev.removes.push(`#${k}`)
}
return prev
},
{ sets: [], removes: [], names: {}, values: {} } as TempType<T>
)
let expression = exp.sets.length ? `SET ${exp.sets.join(', ')}` : ''
expression += exp.removes.length ? ` REMOVE ${exp.removes.join(', ')}` : ''
return {
operation: 'UpdateItem',
key: util.dynamodb.toMapValues(key),
update: {
expression,
expressionNames: exp.names,
expressionValues: util.dynamodb.toMapValues(exp.values),
},
...otherParams,
}
}
export function query<T, K = {}>(
expression: Expression<T & K>,
otherParams?: Omit<DynamoDBQueryRequest, 'query' | 'operation'>
): DynamoDBQueryRequest {
return {
operation: 'Query',
query: getExpression(expression),
...otherParams,
}
}
function _deleteOp<T extends {}, K = {}>(
key: K & Partial<T>,
otherParams?: Pick<
DynamoDBDeleteItemRequest,
'condition' | 'customPartitionKey' | 'populateIndexFields' | '_version'
>
): DynamoDBDeleteItemRequest {
return {
operation: 'DeleteItem',
key: util.dynamodb.toMapValues(key),
...otherParams,
}
}
export function invoke(payload: any): LambdaRequest {
return {
operation: 'Invoke',
payload,
}
}
export type Context<A> = Omit<_Context, 'args' | 'arguments'> & {
args: A
arguments: A
}
export type BlankContext = _Context
export { _deleteOp as delete }
//
export type AppSyncIdentityIAM = {
/**
* The AWS account ID of the caller.
*/
accountId: string
/**
* The Amazon Cognito identity pool ID associated with the caller.
*/
cognitoIdentityPoolId: string
/**
* The Amazon Cognito identity ID of the caller.
*/
cognitoIdentityId: string
/**
* The source IP address of the caller that AWS AppSync receives. If the request doesn't
* include the `x-forwarded-for` header, the source IP value contains only a single IP address
* from the TCP connection. If the request includes a `x-forwarded-for` header, the source IP
* is a list of IP addresses from the `x-forwarded-for` header, in addition to the IP address
* from the TCP connection.
*/
sourceIp: string[]
/**
* The user name of the authenticated user. In the case of `AMAZON_COGNITO_USER_POOLS`
* authorization, the value of username is the value of attribute `cognito:username`. In the
* case of `AWS_IAM` authorization, the value of username is the value of the AWS user
* principal. If you're using IAM authorization with credentials vended from Amazon Cognito
* identity pools, we recommend that you use `cognitoIdentityId`.
*/
username: string
/**
* The Amazon Resource Name (ARN) of the IAM user.
*/
userArn: string
/**
* Either authenticated or unauthenticated based on the identity type.
*/
cognitoIdentityAuthType: string
/**
* A comma-separated list of external identity provider information used in obtaining the
* credentials used to sign the request.
*/
cognitoIdentityAuthProvider: string
}
export type AppSyncIdentityCognito = {
/**
* The source IP address of the caller that AWS AppSync receives. If the request doesn't
* include the `x-forwarded-for` header, the source IP value contains only a single IP address
* from the TCP connection. If the request includes a `x-forwarded-for` header, the source IP
* is a list of IP addresses from the `x-forwarded-for` header, in addition to the IP address
* from the TCP connection.
*/
sourceIp: string[]
/**
* The user name of the authenticated user. In the case of `AMAZON_COGNITO_USER_POOLS`
* authorization, the value of username is the value of attribute `cognito:username`. In the
* case of `AWS_IAM` authorization, the value of username is the value of the AWS user
* principal. If you're using IAM authorization with credentials vended from Amazon Cognito
* identity pools, we recommend that you use `cognitoIdentityId`.
*/
username: string
/**
* The groups the authenticated user belongs to.
*/
groups: string[] | null
/**
* The UUID of the authenticated user.
*/
sub: string
/**
* The token issuer.
*/
issuer: string
/**
* The claims that the user has.
*/
claims: any
/**
* The default authorization strategy for this caller (ALLOW or DENY).
*/
defaultAuthStrategy: string
}
export type AppSyncIdentityOIDC = {
/**
* The UUID of the authenticated user.
*/
sub: string
/**
* The token issuer.
*/
issuer: string
/**
* The claims that the user has.
*/
claims: any
}
export type AppSyncIdentityLambda = {
/**
* content returned by the Lambda function authorizing the request.
*/
resolverContext: any
}
export type Result<T> = Omit<T, '__typename'>
export function redactItem<T>(item: T, redact: Array<keyof T>) {
redact.forEach((r) => delete item[r])
return item
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment