diff --git a/.gitignore b/.gitignore index b0e6bf79..293cb53c 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,5 @@ dist # Terraform cache terraform/.terraform .tool-versions + +localstack/volume diff --git a/api/db/migrations/20231211050651_update_user_database_timestamps/migration.sql b/api/db/migrations/20231211050651_update_user_database_timestamps/migration.sql new file mode 100644 index 00000000..bcc37f30 --- /dev/null +++ b/api/db/migrations/20231211050651_update_user_database_timestamps/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "createdAt" SET DATA TYPE TIMESTAMPTZ(6), +ALTER COLUMN "updatedAt" SET DATA TYPE TIMESTAMPTZ(6); diff --git a/api/db/migrations/20231212224549_update_organization_id_to_optional_for_user/migration.sql b/api/db/migrations/20231212224549_update_organization_id_to_optional_for_user/migration.sql new file mode 100644 index 00000000..1316b69b --- /dev/null +++ b/api/db/migrations/20231212224549_update_organization_id_to_optional_for_user/migration.sql @@ -0,0 +1,8 @@ +-- DropForeignKey +ALTER TABLE "User" DROP CONSTRAINT "User_organizationId_fkey"; + +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "organizationId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "User" ADD CONSTRAINT "User_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/api/db/schema.prisma b/api/db/schema.prisma index e8face87..75dd3ac2 100644 --- a/api/db/schema.prisma +++ b/api/db/schema.prisma @@ -38,12 +38,12 @@ model User { email String name String? agencyId Int? - organizationId Int + organizationId Int? roleId Int? - createdAt DateTime @default(now()) - updatedAt DateTime + createdAt DateTime @default(now()) @db.Timestamptz(6) + updatedAt DateTime @updatedAt @db.Timestamptz(6) agency Agency? @relation(fields: [agencyId], references: [id]) - organization Organization @relation(fields: [organizationId], references: [id]) + organization Organization? @relation(fields: [organizationId], references: [id]) role Role? @relation(fields: [roleId], references: [id]) certified ReportingPeriod[] uploaded Upload[] diff --git a/api/package.json b/api/package.json index 3354f1ec..4096791f 100644 --- a/api/package.json +++ b/api/package.json @@ -3,8 +3,12 @@ "version": "0.0.0", "private": true, "dependencies": { + "@aws-sdk/client-s3": "^3.472.0", + "@aws-sdk/client-ses": "^3.470.0", + "@aws-sdk/client-sqs": "^3.470.0", "@aws-sdk/client-ssm": "^3.462.0", "@aws-sdk/rds-signer": "^3.462.0", + "@aws-sdk/s3-request-presigner": "^3.472.0", "@opentelemetry/instrumentation": "^0.45.1", "@prisma/instrumentation": "^5.7.0", "@redwoodjs/api": "6.4.2", diff --git a/api/src/graphql/users.sdl.ts b/api/src/graphql/users.sdl.ts index 7773b75b..a80124a7 100644 --- a/api/src/graphql/users.sdl.ts +++ b/api/src/graphql/users.sdl.ts @@ -12,10 +12,14 @@ export const schema = gql` organization: Organization! role: Role certified: [ReportingPeriod]! + uploaded: [Upload]! + validated: [UploadValidation]! + invalidated: [UploadValidation]! } type Query { users: [User!]! @requireAuth + usersByOrganization(organizationId: Int!): [User!]! @requireAuth user(id: Int!): User @requireAuth } @@ -23,7 +27,6 @@ export const schema = gql` email: String! name: String agencyId: Int - organizationId: Int! roleId: Int } @@ -31,7 +34,6 @@ export const schema = gql` email: String name: String agencyId: Int - organizationId: Int roleId: Int } diff --git a/api/src/lib/aws.ts b/api/src/lib/aws.ts new file mode 100644 index 00000000..bb619a5b --- /dev/null +++ b/api/src/lib/aws.ts @@ -0,0 +1,106 @@ +import { + GetObjectCommand, + PutObjectCommand, + PutObjectCommandInput, + S3Client, +} from '@aws-sdk/client-s3' +import {ReceiveMessageCommand, SendMessageCommand, SQSClient} from '@aws-sdk/client-sqs' +import {getSignedUrl as awsGetSignedUrl} from '@aws-sdk/s3-request-presigner' + +function getS3Client() { + let s3: S3Client; + if (process.env.LOCALSTACK_HOSTNAME) { + /* + 1. Make sure the local environment has awslocal installed. + 2. Use the commands to create a bucket to test with. + - awslocal s3api create-bucket --bucket arpa-audit-reports --region us-west-2 --create-bucket-configuration '{"LocationConstraint": "us-west-2"}' + 3. Access bucket resource metadata through the following URL. + - awslocal s3api list-buckets + - awslocal s3api list-objects --bucket arpa-audit-reports + */ + console.log('------------ USING LOCALSTACK ------------'); + const endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT || 4566}`; + console.log(`endpoint: ${endpoint}`); + s3 = new S3Client({ + endpoint, + forcePathStyle: true, + region: process.env.AWS_DEFAULT_REGION, + }); + } else { + s3 = new S3Client(); + } + return s3; +} + +async function sendPutObjectToS3Bucket(bucketName: string, key: string, body: any) { + const s3 = getS3Client(); + const uploadParams : PutObjectCommandInput = { + Bucket: bucketName, + Key: key, + Body: body, + ServerSideEncryption: 'AES256', + }; + await s3.send(new PutObjectCommand(uploadParams)); +} + +async function sendHeadObjectToS3Bucket(bucketName: string, key: string) { + const s3 = getS3Client(); + const uploadParams : PutObjectCommandInput = { + Bucket: bucketName, + Key: key, + }; + await s3.send(new PutObjectCommand(uploadParams)); +} + +/** + * This function is a wrapper around the getSignedUrl function from the @aws-sdk/s3-request-presigner package. + * Exists to organize the imports and to make it easier to mock in tests. + */ +async function getSignedUrl(bucketName: string, key: string) { + const s3 = getS3Client(); + const baseParams = { Bucket: bucketName, Key: key }; + return awsGetSignedUrl(s3, new GetObjectCommand(baseParams), { expiresIn: 60 }); +} + +function getSQSClient() { + let sqs: SQSClient; + if (process.env.LOCALSTACK_HOSTNAME) { + console.log('------------ USING LOCALSTACK FOR SQS ------------'); + const endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:${process.env.EDGE_PORT || 4566}`; + sqs = new SQSClient({ endpoint, region: process.env.AWS_DEFAULT_REGION }); + } else { + sqs = new SQSClient(); + } + return sqs; +} + +async function sendSqsMessage(queueUrl: string, messageBody: any) { + const sqs = getSQSClient(); + await sqs.send(new SendMessageCommand({ + QueueUrl: queueUrl, + MessageBody: JSON.stringify(messageBody), + })); +} + +async function receiveSqsMessage(queueUrl: string) { + const sqs = getSQSClient(); + // const receiveResp = await sqs.send(new ReceiveMessageCommand({ + // QueueUrl: process.env.TASK_QUEUE_URL, WaitTimeSeconds: 20, MaxNumberOfMessages: 1, + // })); + + // const receiveResp = await sqs.send(new ReceiveMessageCommand({ + // QueueUrl: process.env.TASK_QUEUE_URL, WaitTimeSeconds: 20, MaxNumberOfMessages: 1, + // })); + + await sqs.send(new ReceiveMessageCommand({ + QueueUrl: queueUrl, WaitTimeSeconds: 20, MaxNumberOfMessages: 1, + })); +} + +export default { + sendPutObjectToS3Bucket, + sendHeadObjectToS3Bucket, + getSignedUrl, + sendSqsMessage, + receiveSqsMessage, +}; diff --git a/api/src/services/users/users.scenarios.ts b/api/src/services/users/users.scenarios.ts index 8cf2a34e..a36be647 100644 --- a/api/src/services/users/users.scenarios.ts +++ b/api/src/services/users/users.scenarios.ts @@ -7,14 +7,14 @@ export const standard = defineScenario({ one: { data: { email: 'String', - updatedAt: '2023-12-07T18:20:20.679Z', + updatedAt: '2023-12-10T00:37:26.049Z', organization: { create: { name: 'String' } }, }, }, two: { data: { email: 'String', - updatedAt: '2023-12-07T18:20:20.679Z', + updatedAt: '2023-12-10T00:37:26.049Z', organization: { create: { name: 'String' } }, }, }, diff --git a/api/src/services/users/users.test.ts b/api/src/services/users/users.test.ts index db6e89e8..0fafc9c0 100644 --- a/api/src/services/users/users.test.ts +++ b/api/src/services/users/users.test.ts @@ -27,13 +27,13 @@ describe('users', () => { input: { email: 'String', organizationId: scenario.user.two.organizationId, - updatedAt: '2023-12-07T18:20:20.664Z', + updatedAt: '2023-12-10T00:37:26.029Z', }, }) expect(result.email).toEqual('String') expect(result.organizationId).toEqual(scenario.user.two.organizationId) - expect(result.updatedAt).toEqual(new Date('2023-12-07T18:20:20.664Z')) + expect(result.updatedAt).toEqual(new Date('2023-12-10T00:37:26.029Z')) }) scenario('updates a user', async (scenario: StandardScenario) => { diff --git a/api/src/services/users/users.ts b/api/src/services/users/users.ts index 55873686..b0308888 100644 --- a/api/src/services/users/users.ts +++ b/api/src/services/users/users.ts @@ -35,6 +35,20 @@ export const deleteUser: MutationResolvers['deleteUser'] = ({ id }) => { }) } +export const usersByOrganization: QueryResolvers['usersByOrganization'] = + async ({ organizationId }) => { + try { + const users = await db.user.findMany({ + where: { organizationId }, + }) + return users || [] // Return an empty array if null is received + } catch (error) { + console.error(error) + // Handle the error appropriately; maybe log it and return an empty array + return [] + } + } + export const User: UserRelationResolvers = { agency: (_obj, { root }) => { return db.user.findUnique({ where: { id: root?.id } }).agency() @@ -48,4 +62,13 @@ export const User: UserRelationResolvers = { certified: (_obj, { root }) => { return db.user.findUnique({ where: { id: root?.id } }).certified() }, + uploaded: (_obj, { root }) => { + return db.user.findUnique({ where: { id: root?.id } }).uploaded() + }, + validated: (_obj, { root }) => { + return db.user.findUnique({ where: { id: root?.id } }).validated() + }, + invalidated: (_obj, { root }) => { + return db.user.findUnique({ where: { id: root?.id } }).invalidated() + }, } diff --git a/api/types/graphql.d.ts b/api/types/graphql.d.ts index 51afe9a3..91263a27 100644 --- a/api/types/graphql.d.ts +++ b/api/types/graphql.d.ts @@ -136,7 +136,6 @@ export type CreateUserInput = { agencyId?: InputMaybe; email: Scalars['String']; name?: InputMaybe; - organizationId: Scalars['Int']; roleId?: InputMaybe; }; @@ -460,6 +459,7 @@ export type Query = { uploads: Array; user?: Maybe; users: Array; + usersByOrganization: Array; }; @@ -540,6 +540,12 @@ export type QueryuserArgs = { id: Scalars['Int']; }; + +/** About the Redwood queries. */ +export type QueryusersByOrganizationArgs = { + organizationId: Scalars['Int']; +}; + /** * The RedwoodJS Root Schema * @@ -689,7 +695,6 @@ export type UpdateUserInput = { agencyId?: InputMaybe; email?: InputMaybe; name?: InputMaybe; - organizationId?: InputMaybe; roleId?: InputMaybe; }; @@ -743,12 +748,15 @@ export type User = { createdAt: Scalars['DateTime']; email: Scalars['String']; id: Scalars['Int']; + invalidated: Array>; name?: Maybe; organization: Organization; organizationId: Scalars['Int']; role?: Maybe; roleId?: Maybe; updatedAt: Scalars['DateTime']; + uploaded: Array>; + validated: Array>; }; type MaybeOrArrayOfMaybe = T | Maybe | Maybe[]; @@ -1184,6 +1192,7 @@ export type QueryResolvers, ParentType, ContextType>; user: Resolver, ParentType, ContextType, RequireFields>; users: OptArgsResolverFn, ParentType, ContextType>; + usersByOrganization: Resolver, ParentType, ContextType, RequireFields>; }; export type QueryRelationResolvers = { @@ -1213,6 +1222,7 @@ export type QueryRelationResolvers, ParentType, ContextType>; user?: RequiredResolverFn, ParentType, ContextType, RequireFields>; users?: RequiredResolverFn, ParentType, ContextType>; + usersByOrganization?: RequiredResolverFn, ParentType, ContextType, RequireFields>; }; export type RedwoodResolvers = { @@ -1412,12 +1422,15 @@ export type UserResolvers; email: OptArgsResolverFn; id: OptArgsResolverFn; + invalidated: OptArgsResolverFn>, ParentType, ContextType>; name: OptArgsResolverFn, ParentType, ContextType>; organization: OptArgsResolverFn; organizationId: OptArgsResolverFn; role: OptArgsResolverFn, ParentType, ContextType>; roleId: OptArgsResolverFn, ParentType, ContextType>; updatedAt: OptArgsResolverFn; + uploaded: OptArgsResolverFn>, ParentType, ContextType>; + validated: OptArgsResolverFn>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -1428,12 +1441,15 @@ export type UserRelationResolvers; email?: RequiredResolverFn; id?: RequiredResolverFn; + invalidated?: RequiredResolverFn>, ParentType, ContextType>; name?: RequiredResolverFn, ParentType, ContextType>; organization?: RequiredResolverFn; organizationId?: RequiredResolverFn; role?: RequiredResolverFn, ParentType, ContextType>; roleId?: RequiredResolverFn, ParentType, ContextType>; updatedAt?: RequiredResolverFn; + uploaded?: RequiredResolverFn>, ParentType, ContextType>; + validated?: RequiredResolverFn>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; diff --git a/localstack/docker-compose.yml b/localstack/docker-compose.yml new file mode 100644 index 00000000..fb3e7b9b --- /dev/null +++ b/localstack/docker-compose.yml @@ -0,0 +1,17 @@ +version: "3.8" + +services: + localstack: + container_name: "cpf-reporter-localstack-main" + image: localstack/localstack + ports: + - "4566:4566" # LocalStack Gateway + - "4510-4559:4510-4559" # external services port range + environment: + - DEBUG=${DEBUG-} + - DOCKER_HOST=unix:///var/run/docker.sock + - AWS_DEFAULT_REGION=${AWS_REGION:-us-west-2} + volumes: + - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" + - "/var/run/docker.sock:/var/run/docker.sock" + - "./entrypoint/init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh" diff --git a/localstack/entrypoint/init-aws.sh b/localstack/entrypoint/init-aws.sh new file mode 100755 index 00000000..a1eda7fb --- /dev/null +++ b/localstack/entrypoint/init-aws.sh @@ -0,0 +1,16 @@ +#! /bin/bash + +export AWS_ACCESS_KEY_ID="test" +export AWS_SECRET_ACCESS_KEY="test" + +VALID_EMAILS=( + "grants-identification@usdigitalresponse.org" +) + +for email in "${VALID_EMAILS[@]}"; do + awslocal ses verify-email-identity --email-address ${email} + echo "Verified ${email} to send with localstack SES" +done + +awslocal s3api create-bucket --bucket cpf-reporter --region us-west-2 --create-bucket-configuration '{"LocationConstraint": "us-west-2"}' + diff --git a/web/src/Routes.tsx b/web/src/Routes.tsx index 8e5a09ee..9a73e69a 100644 --- a/web/src/Routes.tsx +++ b/web/src/Routes.tsx @@ -27,6 +27,11 @@ const Routes = () => { + {/* Users */} + + + + {/* Reporting Periods */} {/* Organizations */} diff --git a/web/src/components/Navigation/Navigation.tsx b/web/src/components/Navigation/Navigation.tsx index aeab9736..afba5759 100644 --- a/web/src/components/Navigation/Navigation.tsx +++ b/web/src/components/Navigation/Navigation.tsx @@ -27,10 +27,7 @@ const Navigation = () => {
Subrecipients
- -
Users
-
- {/* TODO: Replace disabled tabs with the tabs below when Users and Subrecipients pages are ready */} + {/* TODO: Use the code below when "Subrecipients" page is ready */} {/* { Subrecipients */} - {/* + { > Users - */} +
Loading...
+ +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ user }: CellSuccessProps) => { + const [updateUser, { loading, error }] = useMutation(UPDATE_USER_MUTATION, { + onCompleted: () => { + toast.success('User updated') + navigate(routes.users()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onSave = (input: UpdateUserInput, id: EditUserById['user']['id']) => { + updateUser({ variables: { id, input } }) + } + + return ( +
+
+

+ Edit User {user?.id} +

+
+
+ +
+
+ ) +} diff --git a/web/src/components/User/NewUser/NewUser.tsx b/web/src/components/User/NewUser/NewUser.tsx new file mode 100644 index 00000000..150bea77 --- /dev/null +++ b/web/src/components/User/NewUser/NewUser.tsx @@ -0,0 +1,44 @@ +import type { CreateUserInput } from 'types/graphql' + +import { navigate, routes } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import UserForm from 'src/components/User/UserForm' + +const CREATE_USER_MUTATION = gql` + mutation CreateUserMutation($input: CreateUserInput!) { + createUser(input: $input) { + id + } + } +` + +const NewUser = () => { + const [createUser, { loading, error }] = useMutation(CREATE_USER_MUTATION, { + onCompleted: () => { + toast.success('User created') + navigate(routes.users()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onSave = (input: CreateUserInput) => { + createUser({ variables: { input } }) + } + + return ( +
+
+

New User

+
+
+ +
+
+ ) +} + +export default NewUser diff --git a/web/src/components/User/User/User.tsx b/web/src/components/User/User/User.tsx new file mode 100644 index 00000000..b03f02f5 --- /dev/null +++ b/web/src/components/User/User/User.tsx @@ -0,0 +1,102 @@ +import type { DeleteUserMutationVariables, FindUserById } from 'types/graphql' + +import { Link, routes, navigate } from '@redwoodjs/router' +import { useMutation } from '@redwoodjs/web' +import { toast } from '@redwoodjs/web/toast' + +import { timeTag } from 'src/lib/formatters' + +const DELETE_USER_MUTATION = gql` + mutation DeleteUserMutation($id: Int!) { + deleteUser(id: $id) { + id + } + } +` + +interface Props { + user: NonNullable +} + +const User = ({ user }: Props) => { + const [deleteUser] = useMutation(DELETE_USER_MUTATION, { + onCompleted: () => { + toast.success('User deleted') + navigate(routes.users()) + }, + onError: (error) => { + toast.error(error.message) + }, + }) + + const onDeleteClick = (id: DeleteUserMutationVariables['id']) => { + if (confirm('Are you sure you want to delete user ' + id + '?')) { + deleteUser({ variables: { id } }) + } + } + + return ( + <> +
+
+

+ User {user.id} Detail +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Id{user.id}
Email{user.email}
Name{user.name}
Agency id{user.agencyId}
Organization id{user.organizationId}
Role id{user.roleId}
Created at{timeTag(user.createdAt)}
Updated at{timeTag(user.updatedAt)}
+
+ + + ) +} + +export default User diff --git a/web/src/components/User/UserCell/UserCell.tsx b/web/src/components/User/UserCell/UserCell.tsx new file mode 100644 index 00000000..a4757c2f --- /dev/null +++ b/web/src/components/User/UserCell/UserCell.tsx @@ -0,0 +1,32 @@ +import type { FindUserById } from 'types/graphql' + +import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web' + +import User from 'src/components/User/User' + +export const QUERY = gql` + query FindUserById($id: Int!) { + user: user(id: $id) { + id + email + name + agencyId + organizationId + roleId + createdAt + updatedAt + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
User not found
+ +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ user }: CellSuccessProps) => { + return +} diff --git a/web/src/components/User/UserForm/UserForm.tsx b/web/src/components/User/UserForm/UserForm.tsx new file mode 100644 index 00000000..69d65f26 --- /dev/null +++ b/web/src/components/User/UserForm/UserForm.tsx @@ -0,0 +1,188 @@ +import { Button } from 'react-bootstrap' +import { useForm, UseFormReturn } from 'react-hook-form' +import type { EditUserById, UpdateUserInput } from 'types/graphql' + +import { + Form, + FormError, + FieldError, + Label, + TextField, + NumberField, + Submit, +} from '@redwoodjs/forms' +import type { RWGqlError } from '@redwoodjs/forms' + +type FormUser = NonNullable + +interface UserFormProps { + user?: EditUserById['user'] + onSave: (data: UpdateUserInput, id?: FormUser['id']) => void + error: RWGqlError + loading: boolean +} + +const UserForm = (props: UserFormProps) => { + const { user, onSave, error, loading } = props + const formMethods: UseFormReturn = useForm() + const hasErrors = Object.keys(formMethods.formState.errors).length > 0 + + // Resets the form to the previous values when editing the existing user + // Clears out the form when creating a new user + const onReset = () => { + formMethods.reset() + } + const onSubmit = (data: FormUser) => { + onSave(data, props?.user?.id) + } + + return ( + + onSubmit={onSubmit} + formMethods={formMethods} + error={error} + className={hasErrors ? 'was-validated' : ''} + > + {user && ( +
+ +
+ +
+
+ )} + + {user && ( +
+ +
+ +
+
+ )} + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + Save + + +
+
+ + ) +} + +export default UserForm diff --git a/web/src/components/User/Users/Users.tsx b/web/src/components/User/Users/Users.tsx new file mode 100644 index 00000000..c215c3b6 --- /dev/null +++ b/web/src/components/User/Users/Users.tsx @@ -0,0 +1,54 @@ +import Button from 'react-bootstrap/Button' +import Table from 'react-bootstrap/Table' +import type { FindUsersByOrganizationId } from 'types/graphql' + +import { Link, routes } from '@redwoodjs/router' + +import { timeTag, truncate } from 'src/lib/formatters' + +const UsersList = ({ usersByOrganization }: FindUsersByOrganizationId) => { + return ( + + + + + + + + + + + + + {usersByOrganization.map((user) => ( + + + + + + + + + ))} + +
EmailNameAgency idRole idCreated atActions
{truncate(user.email)}{truncate(user.name)} + {truncate(user.agencyId)} + {truncate(user.roleId)} + {timeTag(user.createdAt)} + + +
+ ) +} + +export default UsersList diff --git a/web/src/components/User/UsersCell/UsersCell.tsx b/web/src/components/User/UsersCell/UsersCell.tsx new file mode 100644 index 00000000..c1d86ada --- /dev/null +++ b/web/src/components/User/UsersCell/UsersCell.tsx @@ -0,0 +1,44 @@ +import type { FindUsersByOrganizationId } from 'types/graphql' + +import { Link, routes } from '@redwoodjs/router' +import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web' + +import Users from 'src/components/User/Users' + +export const QUERY = gql` + query FindUsersByOrganizationId($organizationId: Int!) { + usersByOrganization(organizationId: $organizationId) { + id + email + name + agencyId + organizationId + roleId + createdAt + updatedAt + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () => { + return ( +
+ {'No users yet. '} + + {'Create one?'} + +
+ ) +} + +export const Failure = ({ error }: CellFailureProps) => ( +
{error?.message}
+) + +export const Success = ({ + usersByOrganization, +}: CellSuccessProps) => { + return +} diff --git a/web/src/pages/User/EditUserPage/EditUserPage.tsx b/web/src/pages/User/EditUserPage/EditUserPage.tsx new file mode 100644 index 00000000..8e2a3570 --- /dev/null +++ b/web/src/pages/User/EditUserPage/EditUserPage.tsx @@ -0,0 +1,11 @@ +import EditUserCell from 'src/components/User/EditUserCell' + +type UserPageProps = { + id: number +} + +const EditUserPage = ({ id }: UserPageProps) => { + return +} + +export default EditUserPage diff --git a/web/src/pages/User/NewUserPage/NewUserPage.tsx b/web/src/pages/User/NewUserPage/NewUserPage.tsx new file mode 100644 index 00000000..c9b6dceb --- /dev/null +++ b/web/src/pages/User/NewUserPage/NewUserPage.tsx @@ -0,0 +1,7 @@ +import NewUser from 'src/components/User/NewUser' + +const NewUserPage = () => { + return +} + +export default NewUserPage diff --git a/web/src/pages/User/UserPage/UserPage.tsx b/web/src/pages/User/UserPage/UserPage.tsx new file mode 100644 index 00000000..3f298a4e --- /dev/null +++ b/web/src/pages/User/UserPage/UserPage.tsx @@ -0,0 +1,11 @@ +import UserCell from 'src/components/User/UserCell' + +type UserPageProps = { + id: number +} + +const UserPage = ({ id }: UserPageProps) => { + return +} + +export default UserPage diff --git a/web/src/pages/User/UsersPage/UsersPage.tsx b/web/src/pages/User/UsersPage/UsersPage.tsx new file mode 100644 index 00000000..cb573ef1 --- /dev/null +++ b/web/src/pages/User/UsersPage/UsersPage.tsx @@ -0,0 +1,24 @@ +import Button from 'react-bootstrap/Button' + +import { Link, routes } from '@redwoodjs/router' + +import UsersCell from 'src/components/User/UsersCell' + +const UsersPage = () => { + // temporarily hardcode organizationId to 1 + const organizationIdOfUser = 1 + + return ( +
+

Users

+ + + + +
+ ) +} + +export default UsersPage diff --git a/web/types/graphql.d.ts b/web/types/graphql.d.ts index d0d6c99f..12d36d96 100644 --- a/web/types/graphql.d.ts +++ b/web/types/graphql.d.ts @@ -118,7 +118,6 @@ export type CreateUserInput = { agencyId?: InputMaybe; email: Scalars['String']; name?: InputMaybe; - organizationId: Scalars['Int']; roleId?: InputMaybe; }; @@ -442,6 +441,7 @@ export type Query = { uploads: Array; user?: Maybe; users: Array; + usersByOrganization: Array; }; @@ -522,6 +522,12 @@ export type QueryuserArgs = { id: Scalars['Int']; }; + +/** About the Redwood queries. */ +export type QueryusersByOrganizationArgs = { + organizationId: Scalars['Int']; +}; + /** * The RedwoodJS Root Schema * @@ -671,7 +677,6 @@ export type UpdateUserInput = { agencyId?: InputMaybe; email?: InputMaybe; name?: InputMaybe; - organizationId?: InputMaybe; roleId?: InputMaybe; }; @@ -725,12 +730,15 @@ export type User = { createdAt: Scalars['DateTime']; email: Scalars['String']; id: Scalars['Int']; + invalidated: Array>; name?: Maybe; organization: Organization; organizationId: Scalars['Int']; role?: Maybe; roleId?: Maybe; updatedAt: Scalars['DateTime']; + uploaded: Array>; + validated: Array>; }; export type FindAgenciesByOrganizationIdVariables = Exact<{ @@ -869,3 +877,46 @@ export type FindUploadsVariables = Exact<{ [key: string]: never; }>; export type FindUploads = { __typename?: 'Query', uploads: Array<{ __typename?: 'Upload', id: number, filename: string, createdAt: string, updatedAt: string, uploadedBy: { __typename?: 'User', id: number, email: string }, agency: { __typename?: 'Agency', id: number, code: string }, expenditureCategory: { __typename?: 'ExpenditureCategory', id: number, code: string }, validations: Array<{ __typename?: 'UploadValidation', invalidatedAt?: string | null, validatedAt?: string | null } | null> }> }; + +export type EditUserByIdVariables = Exact<{ + id: Scalars['Int']; +}>; + + +export type EditUserById = { __typename?: 'Query', user?: { __typename?: 'User', id: number, email: string, name?: string | null, agencyId?: number | null, organizationId: number, roleId?: number | null, createdAt: string, updatedAt: string } | null }; + +export type UpdateUserMutationVariables = Exact<{ + id: Scalars['Int']; + input: UpdateUserInput; +}>; + + +export type UpdateUserMutation = { __typename?: 'Mutation', updateUser: { __typename?: 'User', id: number, email: string, name?: string | null, agencyId?: number | null, organizationId: number, roleId?: number | null, createdAt: string, updatedAt: string } }; + +export type CreateUserMutationVariables = Exact<{ + input: CreateUserInput; +}>; + + +export type CreateUserMutation = { __typename?: 'Mutation', createUser: { __typename?: 'User', id: number } }; + +export type DeleteUserMutationVariables = Exact<{ + id: Scalars['Int']; +}>; + + +export type DeleteUserMutation = { __typename?: 'Mutation', deleteUser: { __typename?: 'User', id: number } }; + +export type FindUserByIdVariables = Exact<{ + id: Scalars['Int']; +}>; + + +export type FindUserById = { __typename?: 'Query', user?: { __typename?: 'User', id: number, email: string, name?: string | null, agencyId?: number | null, organizationId: number, roleId?: number | null, createdAt: string, updatedAt: string } | null }; + +export type FindUsersByOrganizationIdVariables = Exact<{ + organizationId: Scalars['Int']; +}>; + + +export type FindUsersByOrganizationId = { __typename?: 'Query', usersByOrganization: Array<{ __typename?: 'User', id: number, email: string, name?: string | null, agencyId?: number | null, organizationId: number, roleId?: number | null, createdAt: string, updatedAt: string }> }; diff --git a/yarn.lock b/yarn.lock index 200969e7..f1f38dca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -129,6 +129,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32c@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32c@npm:3.0.0" + dependencies: + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + tslib: ^1.11.1 + checksum: 0a116b5d1c5b09a3dde65aab04a07b32f543e87b68f2d175081e3f4a1a17502343f223d691dd883ace1ddce65cd40093673e7c7415dcd99062202ba87ffb4038 + languageName: node + linkType: hard + "@aws-crypto/ie11-detection@npm:^3.0.0": version: 3.0.0 resolution: "@aws-crypto/ie11-detection@npm:3.0.0" @@ -138,6 +149,21 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/sha1-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha1-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": ^3.0.0 + "@aws-crypto/supports-web-crypto": ^3.0.0 + "@aws-crypto/util": ^3.0.0 + "@aws-sdk/types": ^3.222.0 + "@aws-sdk/util-locate-window": ^3.0.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: 78c379e105a0c4e7b2ed745dffd8f55054d7dde8b350b61de682bbc3cd081a50e2f87861954fa9cd53c7ea711ebca1ca0137b14cb36483efc971f60f573cf129 + languageName: node + linkType: hard + "@aws-crypto/sha256-browser@npm:3.0.0": version: 3.0.0 resolution: "@aws-crypto/sha256-browser@npm:3.0.0" @@ -232,6 +258,169 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-s3@npm:^3.472.0": + version: 3.472.0 + resolution: "@aws-sdk/client-s3@npm:3.472.0" + dependencies: + "@aws-crypto/sha1-browser": 3.0.0 + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.470.0 + "@aws-sdk/core": 3.468.0 + "@aws-sdk/credential-provider-node": 3.470.0 + "@aws-sdk/middleware-bucket-endpoint": 3.470.0 + "@aws-sdk/middleware-expect-continue": 3.468.0 + "@aws-sdk/middleware-flexible-checksums": 3.468.0 + "@aws-sdk/middleware-host-header": 3.468.0 + "@aws-sdk/middleware-location-constraint": 3.468.0 + "@aws-sdk/middleware-logger": 3.468.0 + "@aws-sdk/middleware-recursion-detection": 3.468.0 + "@aws-sdk/middleware-sdk-s3": 3.470.0 + "@aws-sdk/middleware-signing": 3.468.0 + "@aws-sdk/middleware-ssec": 3.468.0 + "@aws-sdk/middleware-user-agent": 3.470.0 + "@aws-sdk/region-config-resolver": 3.470.0 + "@aws-sdk/signature-v4-multi-region": 3.470.0 + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-endpoints": 3.470.0 + "@aws-sdk/util-user-agent-browser": 3.468.0 + "@aws-sdk/util-user-agent-node": 3.470.0 + "@aws-sdk/xml-builder": 3.472.0 + "@smithy/config-resolver": ^2.0.21 + "@smithy/eventstream-serde-browser": ^2.0.15 + "@smithy/eventstream-serde-config-resolver": ^2.0.15 + "@smithy/eventstream-serde-node": ^2.0.15 + "@smithy/fetch-http-handler": ^2.3.1 + "@smithy/hash-blob-browser": ^2.0.16 + "@smithy/hash-node": ^2.0.17 + "@smithy/hash-stream-node": ^2.0.17 + "@smithy/invalid-dependency": ^2.0.15 + "@smithy/md5-js": ^2.0.17 + "@smithy/middleware-content-length": ^2.0.17 + "@smithy/middleware-endpoint": ^2.2.3 + "@smithy/middleware-retry": ^2.0.24 + "@smithy/middleware-serde": ^2.0.15 + "@smithy/middleware-stack": ^2.0.9 + "@smithy/node-config-provider": ^2.1.8 + "@smithy/node-http-handler": ^2.2.1 + "@smithy/protocol-http": ^3.0.11 + "@smithy/smithy-client": ^2.1.18 + "@smithy/types": ^2.7.0 + "@smithy/url-parser": ^2.0.15 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.22 + "@smithy/util-defaults-mode-node": ^2.0.29 + "@smithy/util-endpoints": ^1.0.7 + "@smithy/util-retry": ^2.0.8 + "@smithy/util-stream": ^2.0.23 + "@smithy/util-utf8": ^2.0.2 + "@smithy/util-waiter": ^2.0.15 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 99971908a9da93e5f21e634ee8464686932f6056b053e26d07ee0c217d5b56440616202743c2f0c0f67234e0264fb88a309fee689aa92108a8108b384a9eb117 + languageName: node + linkType: hard + +"@aws-sdk/client-ses@npm:^3.470.0": + version: 3.470.0 + resolution: "@aws-sdk/client-ses@npm:3.470.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.470.0 + "@aws-sdk/core": 3.468.0 + "@aws-sdk/credential-provider-node": 3.470.0 + "@aws-sdk/middleware-host-header": 3.468.0 + "@aws-sdk/middleware-logger": 3.468.0 + "@aws-sdk/middleware-recursion-detection": 3.468.0 + "@aws-sdk/middleware-signing": 3.468.0 + "@aws-sdk/middleware-user-agent": 3.470.0 + "@aws-sdk/region-config-resolver": 3.470.0 + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-endpoints": 3.470.0 + "@aws-sdk/util-user-agent-browser": 3.468.0 + "@aws-sdk/util-user-agent-node": 3.470.0 + "@smithy/config-resolver": ^2.0.21 + "@smithy/fetch-http-handler": ^2.3.1 + "@smithy/hash-node": ^2.0.17 + "@smithy/invalid-dependency": ^2.0.15 + "@smithy/middleware-content-length": ^2.0.17 + "@smithy/middleware-endpoint": ^2.2.3 + "@smithy/middleware-retry": ^2.0.24 + "@smithy/middleware-serde": ^2.0.15 + "@smithy/middleware-stack": ^2.0.9 + "@smithy/node-config-provider": ^2.1.8 + "@smithy/node-http-handler": ^2.2.1 + "@smithy/protocol-http": ^3.0.11 + "@smithy/smithy-client": ^2.1.18 + "@smithy/types": ^2.7.0 + "@smithy/url-parser": ^2.0.15 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.22 + "@smithy/util-defaults-mode-node": ^2.0.29 + "@smithy/util-endpoints": ^1.0.7 + "@smithy/util-retry": ^2.0.8 + "@smithy/util-utf8": ^2.0.2 + "@smithy/util-waiter": ^2.0.15 + fast-xml-parser: 4.2.5 + tslib: ^2.5.0 + checksum: 6d298c9e3bf1fa66561c74f7074c599479d72127af2b087e206427cb0cbe2708ff2366e26db11651864d4e84bf9350311620a81152239e19cb604facd128a586 + languageName: node + linkType: hard + +"@aws-sdk/client-sqs@npm:^3.470.0": + version: 3.470.0 + resolution: "@aws-sdk/client-sqs@npm:3.470.0" + dependencies: + "@aws-crypto/sha256-browser": 3.0.0 + "@aws-crypto/sha256-js": 3.0.0 + "@aws-sdk/client-sts": 3.470.0 + "@aws-sdk/core": 3.468.0 + "@aws-sdk/credential-provider-node": 3.470.0 + "@aws-sdk/middleware-host-header": 3.468.0 + "@aws-sdk/middleware-logger": 3.468.0 + "@aws-sdk/middleware-recursion-detection": 3.468.0 + "@aws-sdk/middleware-sdk-sqs": 3.468.0 + "@aws-sdk/middleware-signing": 3.468.0 + "@aws-sdk/middleware-user-agent": 3.470.0 + "@aws-sdk/region-config-resolver": 3.470.0 + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-endpoints": 3.470.0 + "@aws-sdk/util-user-agent-browser": 3.468.0 + "@aws-sdk/util-user-agent-node": 3.470.0 + "@smithy/config-resolver": ^2.0.21 + "@smithy/fetch-http-handler": ^2.3.1 + "@smithy/hash-node": ^2.0.17 + "@smithy/invalid-dependency": ^2.0.15 + "@smithy/md5-js": ^2.0.17 + "@smithy/middleware-content-length": ^2.0.17 + "@smithy/middleware-endpoint": ^2.2.3 + "@smithy/middleware-retry": ^2.0.24 + "@smithy/middleware-serde": ^2.0.15 + "@smithy/middleware-stack": ^2.0.9 + "@smithy/node-config-provider": ^2.1.8 + "@smithy/node-http-handler": ^2.2.1 + "@smithy/protocol-http": ^3.0.11 + "@smithy/smithy-client": ^2.1.18 + "@smithy/types": ^2.7.0 + "@smithy/url-parser": ^2.0.15 + "@smithy/util-base64": ^2.0.1 + "@smithy/util-body-length-browser": ^2.0.1 + "@smithy/util-body-length-node": ^2.1.0 + "@smithy/util-defaults-mode-browser": ^2.0.22 + "@smithy/util-defaults-mode-node": ^2.0.29 + "@smithy/util-endpoints": ^1.0.7 + "@smithy/util-retry": ^2.0.8 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 3bb10b473cf70a862ab3c4e5ccfbd34cbb18371c03d5b7d6ddc251962c44a5bff8363a71b9823484e0b50c29a80a5bd4c5eb7b072398357dbe9523db5067d735 + languageName: node + linkType: hard + "@aws-sdk/client-ssm@npm:^3.462.0": version: 3.470.0 resolution: "@aws-sdk/client-ssm@npm:3.470.0" @@ -526,6 +715,49 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-bucket-endpoint@npm:3.470.0": + version: 3.470.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.470.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-arn-parser": 3.465.0 + "@smithy/node-config-provider": ^2.1.8 + "@smithy/protocol-http": ^3.0.11 + "@smithy/types": ^2.7.0 + "@smithy/util-config-provider": ^2.0.0 + tslib: ^2.5.0 + checksum: 07b89e68feb6300b904784db09394f8dec9393a6bddde686da40ad63256b274c565fcf8896e6041ae87f677fb5a31aa850902ba50736e882390da848352b54dd + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.468.0": + version: 3.468.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.468.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@smithy/protocol-http": ^3.0.11 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: c92f6bac96c83998e4a7fd2302e8a74ff5550ee5b5e21c477238cb3dcede2e04561e941bb04cc8ff383e208efd33c99d4b0ee254bfd1a2d40b6fd2ecbb53fb1a + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.468.0": + version: 3.468.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.468.0" + dependencies: + "@aws-crypto/crc32": 3.0.0 + "@aws-crypto/crc32c": 3.0.0 + "@aws-sdk/types": 3.468.0 + "@smithy/is-array-buffer": ^2.0.0 + "@smithy/protocol-http": ^3.0.11 + "@smithy/types": ^2.7.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: e352e21d340ae0f011bce52dca00fbfe1f986f4978f54a00c9ffa267344b8d9a1909a41dffc3696bb3c4f968ab39d2922296aebe627bb5b921cd5f995f5d508d + languageName: node + linkType: hard + "@aws-sdk/middleware-host-header@npm:3.468.0": version: 3.468.0 resolution: "@aws-sdk/middleware-host-header@npm:3.468.0" @@ -538,6 +770,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-location-constraint@npm:3.468.0": + version: 3.468.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.468.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 8e9653eb3ec4234ad6807bd8ad82b5e5843665ddf279d9d27cf93d6cdf99992f524f9fa3f67ac8ee3baff8f01984764579ae7644690597325e886038a3f88d72 + languageName: node + linkType: hard + "@aws-sdk/middleware-logger@npm:3.468.0": version: 3.468.0 resolution: "@aws-sdk/middleware-logger@npm:3.468.0" @@ -561,6 +804,36 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-sdk-s3@npm:3.470.0": + version: 3.470.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.470.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-arn-parser": 3.465.0 + "@smithy/node-config-provider": ^2.1.8 + "@smithy/protocol-http": ^3.0.11 + "@smithy/signature-v4": ^2.0.0 + "@smithy/smithy-client": ^2.1.18 + "@smithy/types": ^2.7.0 + "@smithy/util-config-provider": ^2.0.0 + tslib: ^2.5.0 + checksum: 838549873ce99aa79aeb8b08c5c858e8632a29c85d1e0af89c4faf2750496559e089c1e50d06edad67faa1121459f7aeafac78b6c719391371753a6278e05763 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-sqs@npm:3.468.0": + version: 3.468.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.468.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@smithy/types": ^2.7.0 + "@smithy/util-hex-encoding": ^2.0.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 8fbd674363ec9f5b097c15a77c58763d40c8f5e78d3547aae9fd63517afaf1bfdc676fe4ad104762b06399174b36f99107ab931c378f1970f95ce91ac8aa8cd8 + languageName: node + linkType: hard + "@aws-sdk/middleware-sdk-sts@npm:3.468.0": version: 3.468.0 resolution: "@aws-sdk/middleware-sdk-sts@npm:3.468.0" @@ -588,6 +861,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-ssec@npm:3.468.0": + version: 3.468.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.468.0" + dependencies: + "@aws-sdk/types": 3.468.0 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 1861a8c1ea86d9b52a41769eba67669659bc0db28f958ea4aed4aaac6f7f1ce920d0ff669e6be3514609233cdd281c46a0bd1a2c4a13267e18a7d94b31ab85b7 + languageName: node + linkType: hard + "@aws-sdk/middleware-user-agent@npm:3.470.0": version: 3.470.0 resolution: "@aws-sdk/middleware-user-agent@npm:3.470.0" @@ -634,6 +918,36 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/s3-request-presigner@npm:^3.472.0": + version: 3.472.0 + resolution: "@aws-sdk/s3-request-presigner@npm:3.472.0" + dependencies: + "@aws-sdk/signature-v4-multi-region": 3.470.0 + "@aws-sdk/types": 3.468.0 + "@aws-sdk/util-format-url": 3.468.0 + "@smithy/middleware-endpoint": ^2.2.3 + "@smithy/protocol-http": ^3.0.11 + "@smithy/smithy-client": ^2.1.18 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 395356e9677c414709ee650bcba0f0a871311a62ade4fbf58c753926ae3ae8a71eaeec8253f4a524723ce0edacb39b5df766756c1a6fa552b81ce0778020e950 + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.470.0": + version: 3.470.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.470.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": 3.470.0 + "@aws-sdk/types": 3.468.0 + "@smithy/protocol-http": ^3.0.11 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: e9ddc65c8f0d104c11ed7f468e18cbc6411819202452e10e42834a4634e933f3e25fcac503d752a9e97dd82174fefff6a047221e385683a8674cac6fe6093779 + languageName: node + linkType: hard + "@aws-sdk/token-providers@npm:3.470.0": version: 3.470.0 resolution: "@aws-sdk/token-providers@npm:3.470.0" @@ -689,6 +1003,15 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-arn-parser@npm:3.465.0": + version: 3.465.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.465.0" + dependencies: + tslib: ^2.5.0 + checksum: ce2dd638e9b8ef3260ce1c1ae299a4e44cbaa28a07cda9f1033b763cea8b1d901b7a963338e8a172af17074d06811f09605f3f903318c4bd2bbf102e92d02546 + languageName: node + linkType: hard + "@aws-sdk/util-endpoints@npm:3.470.0": version: 3.470.0 resolution: "@aws-sdk/util-endpoints@npm:3.470.0" @@ -759,6 +1082,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/xml-builder@npm:3.472.0": + version: 3.472.0 + resolution: "@aws-sdk/xml-builder@npm:3.472.0" + dependencies: + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: e053e8e247b096e311b717a54d70270ba9fcb4bf049d66b3a1958b2136503ba292adfa7efe694ce76d7cb1b4d0fd838aa44b2a8612210c7e1673c5f27d72e88d + languageName: node + linkType: hard + "@babel/cli@npm:7.23.0": version: 7.23.0 resolution: "@babel/cli@npm:7.23.0" @@ -6178,6 +6511,25 @@ __metadata: languageName: node linkType: hard +"@smithy/chunked-blob-reader-native@npm:^2.0.1": + version: 2.0.1 + resolution: "@smithy/chunked-blob-reader-native@npm:2.0.1" + dependencies: + "@smithy/util-base64": ^2.0.1 + tslib: ^2.5.0 + checksum: db13a380a51ace30c8ed5947ea1b9fa65f5f5d0dbb722b4abc4d19e4a1215979174f35365c692f9fe7d3c116eaaf90dd9fb58e1dcff4fe943fc76d86c7f1798d + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/chunked-blob-reader@npm:2.0.0" + dependencies: + tslib: ^2.5.0 + checksum: a47e5298f0b28e25eaa5825ea9737718f0e2b7cf0f03a49cca186eb5544dd20ac91a2d92069f9805e40e5f3ab34d32f8091853518672fdbca009411179dbeb2a + languageName: node + linkType: hard + "@smithy/config-resolver@npm:^2.0.21": version: 2.0.21 resolution: "@smithy/config-resolver@npm:2.0.21" @@ -6216,6 +6568,49 @@ __metadata: languageName: node linkType: hard +"@smithy/eventstream-serde-browser@npm:^2.0.15": + version: 2.0.15 + resolution: "@smithy/eventstream-serde-browser@npm:2.0.15" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.15 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: a11c3c14c860b88955d99444291e686d99f091ec07506556e609144b9e6b016b8f7a54338f101a237cc7aee0b41ef54b8c95377bebf55933a59e9d62dd56598d + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^2.0.15": + version: 2.0.15 + resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.15" + dependencies: + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 6f0a46c1d0082068ed8a428fd2912d3ae16e5a86229c42089dd5c6a290d3bb2767611082479ae9938cd88c69d7857e97d2dc5ebd4dcdbd3ca7c1998d51512ab7 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^2.0.15": + version: 2.0.15 + resolution: "@smithy/eventstream-serde-node@npm:2.0.15" + dependencies: + "@smithy/eventstream-serde-universal": ^2.0.15 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 8dc874949bd849f992f9371e1df5d9a28472c267395a98c06df8dbf4ed931acabafb51e38ff4dc1c41ed3825ef1956ba3b79e4c72dc34667ca43253f7161863a + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^2.0.15": + version: 2.0.15 + resolution: "@smithy/eventstream-serde-universal@npm:2.0.15" + dependencies: + "@smithy/eventstream-codec": ^2.0.15 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 3728bc6ca7605362afa95b339c19089c0765ec2047f67d22c1d6cb371d5b4438d178919a95452687fb93f8d62bca7900abf1c28bfaf2f76845108c9a9e740e47 + languageName: node + linkType: hard + "@smithy/fetch-http-handler@npm:^2.3.1": version: 2.3.1 resolution: "@smithy/fetch-http-handler@npm:2.3.1" @@ -6229,6 +6624,18 @@ __metadata: languageName: node linkType: hard +"@smithy/hash-blob-browser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/hash-blob-browser@npm:2.0.16" + dependencies: + "@smithy/chunked-blob-reader": ^2.0.0 + "@smithy/chunked-blob-reader-native": ^2.0.1 + "@smithy/types": ^2.7.0 + tslib: ^2.5.0 + checksum: 6f8a3c6492d9e839bf32612ca839a54f3185f85298b5cc03104de08ef757b64663ba9537bdaff7368a83af39d8eba423c13e021acbe03146b52eaebf4ac8a238 + languageName: node + linkType: hard + "@smithy/hash-node@npm:^2.0.17": version: 2.0.17 resolution: "@smithy/hash-node@npm:2.0.17" @@ -6241,6 +6648,17 @@ __metadata: languageName: node linkType: hard +"@smithy/hash-stream-node@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/hash-stream-node@npm:2.0.17" + dependencies: + "@smithy/types": ^2.7.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 1a22d2c3943757db166a510fab4d8771623bd489e1298e36fce92429bf4783391ee4c2b65d45aeeaa234643e61155565caf86e08201cc2ac91cf2bdca36087bd + languageName: node + linkType: hard + "@smithy/invalid-dependency@npm:^2.0.15": version: 2.0.15 resolution: "@smithy/invalid-dependency@npm:2.0.15" @@ -6260,6 +6678,17 @@ __metadata: languageName: node linkType: hard +"@smithy/md5-js@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/md5-js@npm:2.0.17" + dependencies: + "@smithy/types": ^2.7.0 + "@smithy/util-utf8": ^2.0.2 + tslib: ^2.5.0 + checksum: 2c17b0cd5496f647acc5fba40b7113b02375694c30713f1d1e869aa1d4ba7e96119065f86e0a367b1651a0beb242e09f218ccbe8eb7458dc99f65829468a0caf + languageName: node + linkType: hard + "@smithy/middleware-content-length@npm:^2.0.17": version: 2.0.17 resolution: "@smithy/middleware-content-length@npm:2.0.17" @@ -8655,8 +9084,12 @@ __metadata: version: 0.0.0-use.local resolution: "api@workspace:api" dependencies: + "@aws-sdk/client-s3": ^3.472.0 + "@aws-sdk/client-ses": ^3.470.0 + "@aws-sdk/client-sqs": ^3.470.0 "@aws-sdk/client-ssm": ^3.462.0 "@aws-sdk/rds-signer": ^3.462.0 + "@aws-sdk/s3-request-presigner": ^3.472.0 "@opentelemetry/instrumentation": ^0.45.1 "@prisma/instrumentation": ^5.7.0 "@redwoodjs/api": 6.4.2