From 1bb7b59d4b8bd156a075ade9de322ae54ca982b6 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Wed, 12 Feb 2020 13:25:22 +0100 Subject: [PATCH 01/13] Extend schema to allow for presigned URLs for uploading files --- @app/config/src/index.ts | 1 + @app/server/package.json | 4 +- .../src/middleware/installPostGraphile.ts | 6 +- .../src/plugins/CreateUploadUrlPlugin.ts | 37 + data/schema.graphql | 764 +++++++++++++----- yarn.lock | 18 + 6 files changed, 637 insertions(+), 193 deletions(-) create mode 100644 @app/server/src/plugins/CreateUploadUrlPlugin.ts diff --git a/@app/config/src/index.ts b/@app/config/src/index.ts index e6a6ee3f..d0bc389a 100644 --- a/@app/config/src/index.ts +++ b/@app/config/src/index.ts @@ -6,6 +6,7 @@ const packageJson = require("../../../package.json"); export const fromEmail = '"PostGraphile Starter" '; export const awsRegion = "us-east-1"; +export const uploadBucket = "my-bucket-name-here"; export const projectName = packageJson.name.replace(/[-_]/g, " "); export const companyName = projectName; // For copyright ownership export const emailLegalText = diff --git a/@app/server/package.json b/@app/server/package.json index 9a2c5f92..bbc84e51 100644 --- a/@app/server/package.json +++ b/@app/server/package.json @@ -23,6 +23,7 @@ "@types/passport-github2": "^1.2.4", "@types/pg": "^7.14.1", "@types/redis": "^2.8.18", + "@types/uuid": "^7.0.2", "body-parser": "^1.19.0", "chalk": "^4.0.0", "connect-pg-simple": "^6.1.0", @@ -43,7 +44,8 @@ "postgraphile": "^4.7.0", "redis": "^3.0.2", "source-map-support": "^0.5.13", - "tslib": "^1.11.1" + "tslib": "^1.11.1", + "uuid": "^7.0.3" }, "devDependencies": { "@types/node": "^13.13.4", diff --git a/@app/server/src/middleware/installPostGraphile.ts b/@app/server/src/middleware/installPostGraphile.ts index 705d899a..915b43ff 100644 --- a/@app/server/src/middleware/installPostGraphile.ts +++ b/@app/server/src/middleware/installPostGraphile.ts @@ -20,6 +20,7 @@ import PassportLoginPlugin from "../plugins/PassportLoginPlugin"; import PrimaryKeyMutationsOnlyPlugin from "../plugins/PrimaryKeyMutationsOnlyPlugin"; import RemoveQueryQueryPlugin from "../plugins/RemoveQueryQueryPlugin"; import SubscriptionsPlugin from "../plugins/SubscriptionsPlugin"; +import CreateUploadUrlPlugin from "../plugins/CreateUploadUrlPlugin"; import handleErrors from "../utils/handleErrors"; import { getAuthPgPool, getRootPgPool } from "./installDatabasePools"; @@ -183,6 +184,9 @@ export function getPostGraphileOptions({ // Adds custom orders to our GraphQL schema OrdersPlugin, + + // Allows API clients to fetch a pre-signed URL for uploading files + CreateUploadUrlPlugin, ], /* @@ -258,7 +262,7 @@ export function getPostGraphileOptions({ // Use this to tell Passport.js we're logged in login: (user: any) => new Promise((resolve, reject) => { - req.login(user, (err) => (err ? reject(err) : resolve())); + req.login(user, err => (err ? reject(err) : resolve())); }), logout: () => { diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts new file mode 100644 index 00000000..3247fdf0 --- /dev/null +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -0,0 +1,37 @@ +import { awsRegion, uploadBucket } from "@app/config"; +import * as aws from "aws-sdk"; +import { gql, makeExtendSchemaPlugin } from "graphile-utils"; +import uuidv4 from "uuid/v4"; + +const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ + typeDefs: gql` + extend type Mutation { + """ + Get a signed URL for uploading files. It will expire in 60 seconds. + """ + createUploadUrl(contentType: String): String! + } + `, + resolvers: { + Mutation: { + async createUploadUrl(_query, args, _context, _resolveInfo) { + const { contentType } = args; + const s3 = new aws.S3({ + region: awsRegion, + signatureVersion: "v4", + }); + const params = { + Bucket: uploadBucket, + ContentType: contentType, + Key: uuidv4(), // randomly generated file name + Expires: 60, // signed URL will expire in 60 seconds + ACL: "public-read", // uploaded file will be publicly readable + }; + const signedUrl = await s3.getSignedUrlPromise("putObject", params); + return signedUrl; + }, + }, + }, +})); + +export default CreateUploadUrlPlugin; diff --git a/data/schema.graphql b/data/schema.graphql index 1619f4ff..9e0c2015 100644 --- a/data/schema.graphql +++ b/data/schema.graphql @@ -1,4 +1,6 @@ -"""All input for the `acceptInvitationToOrganization` mutation.""" +""" +All input for the `acceptInvitationToOrganization` mutation. +""" input AcceptInvitationToOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -9,7 +11,9 @@ input AcceptInvitationToOrganizationInput { invitationId: UUID! } -"""The output of our `acceptInvitationToOrganization` mutation.""" +""" +The output of our `acceptInvitationToOrganization` mutation. +""" type AcceptInvitationToOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -23,7 +27,9 @@ type AcceptInvitationToOrganizationPayload { query: Query } -"""All input for the `changePassword` mutation.""" +""" +All input for the `changePassword` mutation. +""" input ChangePasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -34,7 +40,9 @@ input ChangePasswordInput { oldPassword: String! } -"""The output of our `changePassword` mutation.""" +""" +The output of our `changePassword` mutation. +""" type ChangePasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -49,7 +57,9 @@ type ChangePasswordPayload { success: Boolean } -"""All input for the `confirmAccountDeletion` mutation.""" +""" +All input for the `confirmAccountDeletion` mutation. +""" input ConfirmAccountDeletionInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -59,7 +69,9 @@ input ConfirmAccountDeletionInput { token: String! } -"""The output of our `confirmAccountDeletion` mutation.""" +""" +The output of our `confirmAccountDeletion` mutation. +""" type ConfirmAccountDeletionPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -74,7 +86,9 @@ type ConfirmAccountDeletionPayload { success: Boolean } -"""All input for the `createOrganization` mutation.""" +""" +All input for the `createOrganization` mutation. +""" input CreateOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -85,7 +99,9 @@ input CreateOrganizationInput { slug: String! } -"""The output of our `createOrganization` mutation.""" +""" +The output of our `createOrganization` mutation. +""" type CreateOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -94,9 +110,13 @@ type CreateOrganizationPayload { clientMutationId: String organization: Organization - """An edge for our `Organization`. May be used by Relay 1.""" + """ + An edge for our `Organization`. May be used by Relay 1. + """ organizationEdge( - """The method to use when ordering `Organization`.""" + """ + The method to use when ordering `Organization`. + """ orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -106,7 +126,9 @@ type CreateOrganizationPayload { query: Query } -"""All input for the create `UserEmail` mutation.""" +""" +All input for the create `UserEmail` mutation. +""" input CreateUserEmailInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -114,11 +136,15 @@ input CreateUserEmailInput { """ clientMutationId: String - """The `UserEmail` to be created by this mutation.""" + """ + The `UserEmail` to be created by this mutation. + """ userEmail: UserEmailInput! } -"""The output of our create `UserEmail` mutation.""" +""" +The output of our create `UserEmail` mutation. +""" type CreateUserEmailPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -131,20 +157,30 @@ type CreateUserEmailPayload { """ query: Query - """Reads a single `User` that is related to this `UserEmail`.""" + """ + Reads a single `User` that is related to this `UserEmail`. + """ user: User - """The `UserEmail` that was created by this mutation.""" + """ + The `UserEmail` that was created by this mutation. + """ userEmail: UserEmail - """An edge for our `UserEmail`. May be used by Relay 1.""" + """ + An edge for our `UserEmail`. May be used by Relay 1. + """ userEmailEdge( - """The method to use when ordering `UserEmail`.""" + """ + The method to use when ordering `UserEmail`. + """ orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } -"""A location in a connection that can be used for resuming pagination.""" +""" +A location in a connection that can be used for resuming pagination. +""" scalar Cursor """ @@ -153,7 +189,9 @@ A point in time as described by the [ISO """ scalar Datetime -"""All input for the `deleteOrganization` mutation.""" +""" +All input for the `deleteOrganization` mutation. +""" input DeleteOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -163,7 +201,9 @@ input DeleteOrganizationInput { organizationId: UUID! } -"""The output of our `deleteOrganization` mutation.""" +""" +The output of our `deleteOrganization` mutation. +""" type DeleteOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -177,7 +217,9 @@ type DeleteOrganizationPayload { query: Query } -"""All input for the `deleteUserAuthentication` mutation.""" +""" +All input for the `deleteUserAuthentication` mutation. +""" input DeleteUserAuthenticationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -187,7 +229,9 @@ input DeleteUserAuthenticationInput { id: UUID! } -"""The output of our delete `UserAuthentication` mutation.""" +""" +The output of our delete `UserAuthentication` mutation. +""" type DeleteUserAuthenticationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -201,14 +245,20 @@ type DeleteUserAuthenticationPayload { """ query: Query - """Reads a single `User` that is related to this `UserAuthentication`.""" + """ + Reads a single `User` that is related to this `UserAuthentication`. + """ user: User - """The `UserAuthentication` that was deleted by this mutation.""" + """ + The `UserAuthentication` that was deleted by this mutation. + """ userAuthentication: UserAuthentication } -"""All input for the `deleteUserEmail` mutation.""" +""" +All input for the `deleteUserEmail` mutation. +""" input DeleteUserEmailInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -218,7 +268,9 @@ input DeleteUserEmailInput { id: UUID! } -"""The output of our delete `UserEmail` mutation.""" +""" +The output of our delete `UserEmail` mutation. +""" type DeleteUserEmailPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -232,20 +284,30 @@ type DeleteUserEmailPayload { """ query: Query - """Reads a single `User` that is related to this `UserEmail`.""" + """ + Reads a single `User` that is related to this `UserEmail`. + """ user: User - """The `UserEmail` that was deleted by this mutation.""" + """ + The `UserEmail` that was deleted by this mutation. + """ userEmail: UserEmail - """An edge for our `UserEmail`. May be used by Relay 1.""" + """ + An edge for our `UserEmail`. May be used by Relay 1. + """ userEmailEdge( - """The method to use when ordering `UserEmail`.""" + """ + The method to use when ordering `UserEmail`. + """ orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } -"""All input for the `forgotPassword` mutation.""" +""" +All input for the `forgotPassword` mutation. +""" input ForgotPasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -255,7 +317,9 @@ input ForgotPasswordInput { email: String! } -"""The output of our `forgotPassword` mutation.""" +""" +The output of our `forgotPassword` mutation. +""" type ForgotPasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -269,7 +333,9 @@ type ForgotPasswordPayload { query: Query } -"""All input for the `inviteToOrganization` mutation.""" +""" +All input for the `inviteToOrganization` mutation. +""" input InviteToOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -281,7 +347,9 @@ input InviteToOrganizationInput { username: String } -"""The output of our `inviteToOrganization` mutation.""" +""" +The output of our `inviteToOrganization` mutation. +""" type InviteToOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -308,7 +376,9 @@ type LogoutPayload { success: Boolean } -"""All input for the `makeEmailPrimary` mutation.""" +""" +All input for the `makeEmailPrimary` mutation. +""" input MakeEmailPrimaryInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -318,7 +388,9 @@ input MakeEmailPrimaryInput { emailId: UUID! } -"""The output of our `makeEmailPrimary` mutation.""" +""" +The output of our `makeEmailPrimary` mutation. +""" type MakeEmailPrimaryPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -331,13 +403,19 @@ type MakeEmailPrimaryPayload { """ query: Query - """Reads a single `User` that is related to this `UserEmail`.""" + """ + Reads a single `User` that is related to this `UserEmail`. + """ user: User userEmail: UserEmail - """An edge for our `UserEmail`. May be used by Relay 1.""" + """ + An edge for our `UserEmail`. May be used by Relay 1. + """ userEmailEdge( - """The method to use when ordering `UserEmail`.""" + """ + The method to use when ordering `UserEmail`. + """ orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } @@ -358,7 +436,9 @@ type Mutation { input: AcceptInvitationToOrganizationInput! ): AcceptInvitationToOrganizationPayload - """Enter your old password and a new password to change your password.""" + """ + Enter your old password and a new password to change your password. + """ changePassword( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -391,7 +471,9 @@ type Mutation { input: CreateOrganizationInput! ): CreateOrganizationPayload - """Creates a single `UserEmail`.""" + """ + Creates a single `UserEmail`. + """ createUserEmail( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -409,7 +491,9 @@ type Mutation { input: DeleteOrganizationInput! ): DeleteOrganizationPayload - """Deletes a single `UserAuthentication` using a unique key.""" + """ + Deletes a single `UserAuthentication` using a unique key. + """ deleteUserAuthentication( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -417,7 +501,9 @@ type Mutation { input: DeleteUserAuthenticationInput! ): DeleteUserAuthenticationPayload - """Deletes a single `UserEmail` using a unique key.""" + """ + Deletes a single `UserEmail` using a unique key. + """ deleteUserEmail( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -497,7 +583,9 @@ type Mutation { input: RemoveFromOrganizationInput! ): RemoveFromOrganizationPayload - """Begin the account deletion flow by requesting the confirmation email""" + """ + Begin the account deletion flow by requesting the confirmation email + """ requestAccountDeletion( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -551,7 +639,9 @@ type Mutation { input: TransferOrganizationOwnershipInput! ): TransferOrganizationOwnershipPayload - """Updates a single `Organization` using a unique key and a patch.""" + """ + Updates a single `Organization` using a unique key and a patch. + """ updateOrganization( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -559,7 +649,9 @@ type Mutation { input: UpdateOrganizationInput! ): UpdateOrganizationPayload - """Updates a single `User` using a unique key and a patch.""" + """ + Updates a single `User` using a unique key and a patch. + """ updateUser( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -590,10 +682,14 @@ type Organization { Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -601,10 +697,14 @@ type Organization { """ condition: OrganizationInvitationCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -613,7 +713,9 @@ type Organization { """ offset: Int - """The method to use when ordering `OrganizationInvitation`.""" + """ + The method to use when ordering `OrganizationInvitation`. + """ orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection! @@ -621,10 +723,14 @@ type Organization { Reads and enables pagination through a set of `OrganizationMembership`. """ organizationMemberships( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -632,10 +738,14 @@ type Organization { """ condition: OrganizationMembershipCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -644,7 +754,9 @@ type Organization { """ offset: Int - """The method to use when ordering `OrganizationMembership`.""" + """ + The method to use when ordering `OrganizationMembership`. + """ orderBy: [OrganizationMembershipsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationMembershipsConnection! slug: String! @@ -655,10 +767,14 @@ A condition to be used against `Organization` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationCondition { - """Checks for equality with the object’s `id` field.""" + """ + Checks for equality with the object’s `id` field. + """ id: UUID - """Checks for equality with the object’s `slug` field.""" + """ + Checks for equality with the object’s `slug` field. + """ slug: String } @@ -685,27 +801,39 @@ A condition to be used against `OrganizationInvitation` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationInvitationCondition { - """Checks for equality with the object’s `id` field.""" + """ + Checks for equality with the object’s `id` field. + """ id: UUID - """Checks for equality with the object’s `organizationId` field.""" + """ + Checks for equality with the object’s `organizationId` field. + """ organizationId: UUID - """Checks for equality with the object’s `userId` field.""" + """ + Checks for equality with the object’s `userId` field. + """ userId: UUID } -"""A connection to a list of `OrganizationInvitation` values.""" +""" +A connection to a list of `OrganizationInvitation` values. +""" type OrganizationInvitationsConnection { """ A list of edges which contains the `OrganizationInvitation` and cursor to aid in pagination. """ edges: [OrganizationInvitationsEdge!]! - """A list of `OrganizationInvitation` objects.""" + """ + A list of `OrganizationInvitation` objects. + """ nodes: [OrganizationInvitation!]! - """Information to aid in pagination.""" + """ + Information to aid in pagination. + """ pageInfo: PageInfo! """ @@ -714,16 +842,24 @@ type OrganizationInvitationsConnection { totalCount: Int! } -"""A `OrganizationInvitation` edge in the connection.""" +""" +A `OrganizationInvitation` edge in the connection. +""" type OrganizationInvitationsEdge { - """A cursor for use in pagination.""" + """ + A cursor for use in pagination. + """ cursor: Cursor - """The `OrganizationInvitation` at the end of the edge.""" + """ + The `OrganizationInvitation` at the end of the edge. + """ node: OrganizationInvitation! } -"""Methods to use when ordering `OrganizationInvitation`.""" +""" +Methods to use when ordering `OrganizationInvitation`. +""" enum OrganizationInvitationsOrderBy { ID_ASC ID_DESC @@ -760,27 +896,39 @@ A condition to be used against `OrganizationMembership` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationMembershipCondition { - """Checks for equality with the object’s `id` field.""" + """ + Checks for equality with the object’s `id` field. + """ id: UUID - """Checks for equality with the object’s `organizationId` field.""" + """ + Checks for equality with the object’s `organizationId` field. + """ organizationId: UUID - """Checks for equality with the object’s `userId` field.""" + """ + Checks for equality with the object’s `userId` field. + """ userId: UUID } -"""A connection to a list of `OrganizationMembership` values.""" +""" +A connection to a list of `OrganizationMembership` values. +""" type OrganizationMembershipsConnection { """ A list of edges which contains the `OrganizationMembership` and cursor to aid in pagination. """ edges: [OrganizationMembershipsEdge!]! - """A list of `OrganizationMembership` objects.""" + """ + A list of `OrganizationMembership` objects. + """ nodes: [OrganizationMembership!]! - """Information to aid in pagination.""" + """ + Information to aid in pagination. + """ pageInfo: PageInfo! """ @@ -789,16 +937,24 @@ type OrganizationMembershipsConnection { totalCount: Int! } -"""A `OrganizationMembership` edge in the connection.""" +""" +A `OrganizationMembership` edge in the connection. +""" type OrganizationMembershipsEdge { - """A cursor for use in pagination.""" + """ + A cursor for use in pagination. + """ cursor: Cursor - """The `OrganizationMembership` at the end of the edge.""" + """ + The `OrganizationMembership` at the end of the edge. + """ node: OrganizationMembership! } -"""Methods to use when ordering `OrganizationMembership`.""" +""" +Methods to use when ordering `OrganizationMembership`. +""" enum OrganizationMembershipsOrderBy { ID_ASC ID_DESC @@ -821,33 +977,49 @@ input OrganizationPatch { slug: String } -"""A connection to a list of `Organization` values.""" +""" +A connection to a list of `Organization` values. +""" type OrganizationsConnection { """ A list of edges which contains the `Organization` and cursor to aid in pagination. """ edges: [OrganizationsEdge!]! - """A list of `Organization` objects.""" + """ + A list of `Organization` objects. + """ nodes: [Organization!]! - """Information to aid in pagination.""" + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - """The count of *all* `Organization` you could get from the connection.""" + """ + The count of *all* `Organization` you could get from the connection. + """ totalCount: Int! } -"""A `Organization` edge in the connection.""" +""" +A `Organization` edge in the connection. +""" type OrganizationsEdge { - """A cursor for use in pagination.""" + """ + A cursor for use in pagination. + """ cursor: Cursor - """The `Organization` at the end of the edge.""" + """ + The `Organization` at the end of the edge. + """ node: Organization! } -"""Methods to use when ordering `Organization`.""" +""" +Methods to use when ordering `Organization`. +""" enum OrganizationsOrderBy { ID_ASC ID_DESC @@ -858,24 +1030,38 @@ enum OrganizationsOrderBy { SLUG_DESC } -"""Information about pagination in a connection.""" +""" +Information about pagination in a connection. +""" type PageInfo { - """When paginating forwards, the cursor to continue.""" + """ + When paginating forwards, the cursor to continue. + """ endCursor: Cursor - """When paginating forwards, are there more items?""" + """ + When paginating forwards, are there more items? + """ hasNextPage: Boolean! - """When paginating backwards, are there more items?""" + """ + When paginating backwards, are there more items? + """ hasPreviousPage: Boolean! - """When paginating backwards, the cursor to continue.""" + """ + When paginating backwards, the cursor to continue. + """ startCursor: Cursor } -"""The root query type which gives access points into the data universe.""" +""" +The root query type which gives access points into the data universe. +""" type Query { - """The currently logged in user (or null if not logged in).""" + """ + The currently logged in user (or null if not logged in). + """ currentUser: User organization(id: UUID!): Organization organizationBySlug(slug: String!): Organization @@ -891,10 +1077,14 @@ type Query { Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -902,10 +1092,14 @@ type Query { """ condition: OrganizationInvitationCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -914,17 +1108,25 @@ type Query { """ offset: Int - """The method to use when ordering `OrganizationInvitation`.""" + """ + The method to use when ordering `OrganizationInvitation`. + """ orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection organizationMembership(id: UUID!): OrganizationMembership - """Reads and enables pagination through a set of `Organization`.""" + """ + Reads and enables pagination through a set of `Organization`. + """ organizations( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -932,10 +1134,14 @@ type Query { """ condition: OrganizationCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -944,7 +1150,9 @@ type Query { """ offset: Int - """The method to use when ordering `Organization`.""" + """ + The method to use when ordering `Organization`. + """ orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsConnection user(id: UUID!): User @@ -965,7 +1173,9 @@ type RegisterPayload { user: User! } -"""All input for the `removeFromOrganization` mutation.""" +""" +All input for the `removeFromOrganization` mutation. +""" input RemoveFromOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -976,7 +1186,9 @@ input RemoveFromOrganizationInput { userId: UUID! } -"""The output of our `removeFromOrganization` mutation.""" +""" +The output of our `removeFromOrganization` mutation. +""" type RemoveFromOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -990,7 +1202,9 @@ type RemoveFromOrganizationPayload { query: Query } -"""All input for the `requestAccountDeletion` mutation.""" +""" +All input for the `requestAccountDeletion` mutation. +""" input RequestAccountDeletionInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -999,7 +1213,9 @@ input RequestAccountDeletionInput { clientMutationId: String } -"""The output of our `requestAccountDeletion` mutation.""" +""" +The output of our `requestAccountDeletion` mutation. +""" type RequestAccountDeletionPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1014,7 +1230,9 @@ type RequestAccountDeletionPayload { success: Boolean } -"""All input for the `resendEmailVerificationCode` mutation.""" +""" +All input for the `resendEmailVerificationCode` mutation. +""" input ResendEmailVerificationCodeInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1024,7 +1242,9 @@ input ResendEmailVerificationCodeInput { emailId: UUID! } -"""The output of our `resendEmailVerificationCode` mutation.""" +""" +The output of our `resendEmailVerificationCode` mutation. +""" type ResendEmailVerificationCodePayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1039,7 +1259,9 @@ type ResendEmailVerificationCodePayload { success: Boolean } -"""All input for the `resetPassword` mutation.""" +""" +All input for the `resetPassword` mutation. +""" input ResetPasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1051,7 +1273,9 @@ input ResetPasswordInput { userId: UUID! } -"""The output of our `resetPassword` mutation.""" +""" +The output of our `resetPassword` mutation. +""" type ResetPasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1070,11 +1294,15 @@ type ResetPasswordPayload { The root subscription type: contains realtime events you can subscribe to with the `subscription` operation. """ type Subscription { - """Triggered when the logged in user's record is updated in some way.""" + """ + Triggered when the logged in user's record is updated in some way. + """ currentUserUpdated: UserSubscriptionPayload } -"""All input for the `transferOrganizationBillingContact` mutation.""" +""" +All input for the `transferOrganizationBillingContact` mutation. +""" input TransferOrganizationBillingContactInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1085,7 +1313,9 @@ input TransferOrganizationBillingContactInput { userId: UUID! } -"""The output of our `transferOrganizationBillingContact` mutation.""" +""" +The output of our `transferOrganizationBillingContact` mutation. +""" type TransferOrganizationBillingContactPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1094,9 +1324,13 @@ type TransferOrganizationBillingContactPayload { clientMutationId: String organization: Organization - """An edge for our `Organization`. May be used by Relay 1.""" + """ + An edge for our `Organization`. May be used by Relay 1. + """ organizationEdge( - """The method to use when ordering `Organization`.""" + """ + The method to use when ordering `Organization`. + """ orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1106,7 +1340,9 @@ type TransferOrganizationBillingContactPayload { query: Query } -"""All input for the `transferOrganizationOwnership` mutation.""" +""" +All input for the `transferOrganizationOwnership` mutation. +""" input TransferOrganizationOwnershipInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1117,7 +1353,9 @@ input TransferOrganizationOwnershipInput { userId: UUID! } -"""The output of our `transferOrganizationOwnership` mutation.""" +""" +The output of our `transferOrganizationOwnership` mutation. +""" type TransferOrganizationOwnershipPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1126,9 +1364,13 @@ type TransferOrganizationOwnershipPayload { clientMutationId: String organization: Organization - """An edge for our `Organization`. May be used by Relay 1.""" + """ + An edge for our `Organization`. May be used by Relay 1. + """ organizationEdge( - """The method to use when ordering `Organization`.""" + """ + The method to use when ordering `Organization`. + """ orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1138,7 +1380,9 @@ type TransferOrganizationOwnershipPayload { query: Query } -"""All input for the `updateOrganization` mutation.""" +""" +All input for the `updateOrganization` mutation. +""" input UpdateOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1153,7 +1397,9 @@ input UpdateOrganizationInput { patch: OrganizationPatch! } -"""The output of our update `Organization` mutation.""" +""" +The output of our update `Organization` mutation. +""" type UpdateOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1161,12 +1407,18 @@ type UpdateOrganizationPayload { """ clientMutationId: String - """The `Organization` that was updated by this mutation.""" + """ + The `Organization` that was updated by this mutation. + """ organization: Organization - """An edge for our `Organization`. May be used by Relay 1.""" + """ + An edge for our `Organization`. May be used by Relay 1. + """ organizationEdge( - """The method to use when ordering `Organization`.""" + """ + The method to use when ordering `Organization`. + """ orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1176,7 +1428,9 @@ type UpdateOrganizationPayload { query: Query } -"""All input for the `updateUser` mutation.""" +""" +All input for the `updateUser` mutation. +""" input UpdateUserInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1184,7 +1438,9 @@ input UpdateUserInput { """ clientMutationId: String - """Unique identifier for the user.""" + """ + Unique identifier for the user. + """ id: UUID! """ @@ -1193,7 +1449,9 @@ input UpdateUserInput { patch: UserPatch! } -"""The output of our update `User` mutation.""" +""" +The output of our update `User` mutation. +""" type UpdateUserPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1206,41 +1464,61 @@ type UpdateUserPayload { """ query: Query - """The `User` that was updated by this mutation.""" + """ + The `User` that was updated by this mutation. + """ user: User - """An edge for our `User`. May be used by Relay 1.""" + """ + An edge for our `User`. May be used by Relay 1. + """ userEdge( - """The method to use when ordering `User`.""" + """ + The method to use when ordering `User`. + """ orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC] ): UsersEdge } -"""A user who can log in to the application.""" +""" +A user who can log in to the application. +""" type User { - """Optional avatar URL.""" + """ + Optional avatar URL. + """ avatarUrl: String createdAt: Datetime! hasPassword: Boolean - """Unique identifier for the user.""" + """ + Unique identifier for the user. + """ id: UUID! - """If true, the user has elevated privileges.""" + """ + If true, the user has elevated privileges. + """ isAdmin: Boolean! isVerified: Boolean! - """Public-facing name (or pseudonym) of the user.""" + """ + Public-facing name (or pseudonym) of the user. + """ name: String """ Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -1248,10 +1526,14 @@ type User { """ condition: OrganizationInvitationCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -1260,7 +1542,9 @@ type User { """ offset: Int - """The method to use when ordering `OrganizationInvitation`.""" + """ + The method to use when ordering `OrganizationInvitation`. + """ orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection! @@ -1268,10 +1552,14 @@ type User { Reads and enables pagination through a set of `OrganizationMembership`. """ organizationMemberships( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -1279,10 +1567,14 @@ type User { """ condition: OrganizationMembershipCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -1291,34 +1583,50 @@ type User { """ offset: Int - """The method to use when ordering `OrganizationMembership`.""" + """ + The method to use when ordering `OrganizationMembership`. + """ orderBy: [OrganizationMembershipsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationMembershipsConnection! updatedAt: Datetime! - """Reads and enables pagination through a set of `UserAuthentication`.""" + """ + Reads and enables pagination through a set of `UserAuthentication`. + """ userAuthenticationsList( """ A condition to be used in determining which values should be returned by the collection. """ condition: UserAuthenticationCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Skip the first `n` values.""" + """ + Skip the first `n` values. + """ offset: Int - """The method to use when ordering `UserAuthentication`.""" + """ + The method to use when ordering `UserAuthentication`. + """ orderBy: [UserAuthenticationsOrderBy!] ): [UserAuthentication!]! - """Reads and enables pagination through a set of `UserEmail`.""" + """ + Reads and enables pagination through a set of `UserEmail`. + """ userEmails( - """Read all values in the set after (below) this cursor.""" + """ + Read all values in the set after (below) this cursor. + """ after: Cursor - """Read all values in the set before (above) this cursor.""" + """ + Read all values in the set before (above) this cursor. + """ before: Cursor """ @@ -1326,10 +1634,14 @@ type User { """ condition: UserEmailCondition - """Only read the first `n` values of the set.""" + """ + Only read the first `n` values of the set. + """ first: Int - """Only read the last `n` values of the set.""" + """ + Only read the last `n` values of the set. + """ last: Int """ @@ -1338,11 +1650,15 @@ type User { """ offset: Int - """The method to use when ordering `UserEmail`.""" + """ + The method to use when ordering `UserEmail`. + """ orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsConnection! - """Public-facing username (or 'handle') of the user.""" + """ + Public-facing username (or 'handle') of the user. + """ username: String! } @@ -1353,14 +1669,20 @@ type UserAuthentication { createdAt: Datetime! id: UUID! - """A unique identifier for the user within the login service.""" + """ + A unique identifier for the user within the login service. + """ identifier: String! - """The login service used, e.g. `twitter` or `github`.""" + """ + The login service used, e.g. `twitter` or `github`. + """ service: String! updatedAt: Datetime! - """Reads a single `User` that is related to this `UserAuthentication`.""" + """ + Reads a single `User` that is related to this `UserAuthentication`. + """ user: User userId: UUID! } @@ -1370,17 +1692,25 @@ A condition to be used against `UserAuthentication` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input UserAuthenticationCondition { - """Checks for equality with the object’s `id` field.""" + """ + Checks for equality with the object’s `id` field. + """ id: UUID - """Checks for equality with the object’s `service` field.""" + """ + Checks for equality with the object’s `service` field. + """ service: String - """Checks for equality with the object’s `userId` field.""" + """ + Checks for equality with the object’s `userId` field. + """ userId: UUID } -"""Methods to use when ordering `UserAuthentication`.""" +""" +Methods to use when ordering `UserAuthentication`. +""" enum UserAuthenticationsOrderBy { ID_ASC ID_DESC @@ -1393,11 +1723,15 @@ enum UserAuthenticationsOrderBy { USER_ID_DESC } -"""Information about a user's email address.""" +""" +Information about a user's email address. +""" type UserEmail { createdAt: Datetime! - """The users email address, in `a@b.c` format.""" + """ + The users email address, in `a@b.c` format. + """ email: String! id: UUID! isPrimary: Boolean! @@ -1409,7 +1743,9 @@ type UserEmail { isVerified: Boolean! updatedAt: Datetime! - """Reads a single `User` that is related to this `UserEmail`.""" + """ + Reads a single `User` that is related to this `UserEmail`. + """ user: User userId: UUID! } @@ -1419,49 +1755,75 @@ A condition to be used against `UserEmail` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input UserEmailCondition { - """Checks for equality with the object’s `id` field.""" + """ + Checks for equality with the object’s `id` field. + """ id: UUID - """Checks for equality with the object’s `isPrimary` field.""" + """ + Checks for equality with the object’s `isPrimary` field. + """ isPrimary: Boolean - """Checks for equality with the object’s `userId` field.""" + """ + Checks for equality with the object’s `userId` field. + """ userId: UUID } -"""An input for mutations affecting `UserEmail`""" +""" +An input for mutations affecting `UserEmail` +""" input UserEmailInput { - """The users email address, in `a@b.c` format.""" + """ + The users email address, in `a@b.c` format. + """ email: String! } -"""A connection to a list of `UserEmail` values.""" +""" +A connection to a list of `UserEmail` values. +""" type UserEmailsConnection { """ A list of edges which contains the `UserEmail` and cursor to aid in pagination. """ edges: [UserEmailsEdge!]! - """A list of `UserEmail` objects.""" + """ + A list of `UserEmail` objects. + """ nodes: [UserEmail!]! - """Information to aid in pagination.""" + """ + Information to aid in pagination. + """ pageInfo: PageInfo! - """The count of *all* `UserEmail` you could get from the connection.""" + """ + The count of *all* `UserEmail` you could get from the connection. + """ totalCount: Int! } -"""A `UserEmail` edge in the connection.""" +""" +A `UserEmail` edge in the connection. +""" type UserEmailsEdge { - """A cursor for use in pagination.""" + """ + A cursor for use in pagination. + """ cursor: Cursor - """The `UserEmail` at the end of the edge.""" + """ + The `UserEmail` at the end of the edge. + """ node: UserEmail! } -"""Methods to use when ordering `UserEmail`.""" +""" +Methods to use when ordering `UserEmail`. +""" enum UserEmailsOrderBy { ID_ASC ID_DESC @@ -1474,28 +1836,44 @@ enum UserEmailsOrderBy { USER_ID_DESC } -"""Represents an update to a `User`. Fields that are set will be updated.""" +""" +Represents an update to a `User`. Fields that are set will be updated. +""" input UserPatch { - """Optional avatar URL.""" + """ + Optional avatar URL. + """ avatarUrl: String - """Public-facing name (or pseudonym) of the user.""" + """ + Public-facing name (or pseudonym) of the user. + """ name: String - """Public-facing username (or 'handle') of the user.""" + """ + Public-facing username (or 'handle') of the user. + """ username: String } -"""A `User` edge in the connection.""" +""" +A `User` edge in the connection. +""" type UsersEdge { - """A cursor for use in pagination.""" + """ + A cursor for use in pagination. + """ cursor: Cursor - """The `User` at the end of the edge.""" + """ + The `User` at the end of the edge. + """ node: User! } -"""Methods to use when ordering `User`.""" +""" +Methods to use when ordering `User`. +""" enum UsersOrderBy { ID_ASC ID_DESC @@ -1516,7 +1894,9 @@ A universally unique identifier as defined by [RFC 4122](https://tools.ietf.org/ """ scalar UUID -"""All input for the `verifyEmail` mutation.""" +""" +All input for the `verifyEmail` mutation. +""" input VerifyEmailInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1527,7 +1907,9 @@ input VerifyEmailInput { userEmailId: UUID! } -"""The output of our `verifyEmail` mutation.""" +""" +The output of our `verifyEmail` mutation. +""" type VerifyEmailPayload { """ The exact same `clientMutationId` that was provided in the mutation input, diff --git a/yarn.lock b/yarn.lock index b4f1592a..fafecb53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3174,6 +3174,19 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +<<<<<<< HEAD +======= +"@types/uuid@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.2.tgz#d680a9c596ef84abf5c4c07a32ffd66d582526f8" + integrity sha512-8Ly3zIPTnT0/8RCU6Kg/G3uTICf9sRwYOpUzSIM3503tLIKcnJPRuinHhXngJUy2MntrEf6dlpOHXJju90Qh5w== + +"@types/valid-url@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45" + integrity sha1-YPpDXOJL/VuhB7jSqAeWrq86j0U= + +>>>>>>> Extend schema to allow for presigned URLs for uploading files "@types/ws@^6.0.1": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1" @@ -15653,6 +15666,11 @@ uuid@^7.0.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-compile-cache@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" From e96c5e107afd0d87636482ddb6b1701d0d3524b3 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 18 Apr 2020 15:07:59 +0200 Subject: [PATCH 02/13] respond to review comments --- .../src/plugins/CreateUploadUrlPlugin.ts | 110 ++++++++++++++++-- 1 file changed, 103 insertions(+), 7 deletions(-) diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index 3247fdf0..f39b0433 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -3,19 +3,110 @@ import * as aws from "aws-sdk"; import { gql, makeExtendSchemaPlugin } from "graphile-utils"; import uuidv4 from "uuid/v4"; +import { OurGraphQLContext } from "../middleware/installPostGraphile"; + +const ALLOWED_CONTENT_TYPE_ENUM_MAPPING = { + IMAGE_APNG: "image/apng", + IMAGE_BMP: "image/bmp", + IMAGE_GIF: "image/gif", + IMAGE_JPEG: "image/jpeg", + IMAGE_PNG: "image/png", + IMAGE_SVG_XML: "image/svg+xml", + IMAGE_TIFF: "image/tiff", + IMAGE_WEBP: "image/webp", +}; + const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ typeDefs: gql` + """ + The set of content types that we allow users to upload. + """ + enum AllowedUploadContentType { + "image/apng" + IMAGE_APNG + "image/bmp" + IMAGE_BMP + "image/gif" + IMAGE_GIF + "image/jpeg" + IMAGE_JPEG + "image/png" + IMAGE_PNG + "image/svg+xml" + IMAGE_SVG_XML + "image/tiff" + IMAGE_TIFF + "image/webp" + IMAGE_WEBP + } + + """ + All input for the \`createUploadUrl\` mutation. + """ + input CreateUploadUrlInput { + """ + An arbitrary string value with no semantic meaning. Will be included in the + payload verbatim. May be used to track mutations by the client. + """ + clientMutationId: String + """ + You must provide the content type (or MIME type) of the content you intend + to upload. For further information about content types, see + https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types + """ + contentType: AllowedUploadContentType! + } + + """ + The output of our \`createUploadUrl mutation. + """ + type CreateUploadUrlPayload { + """ + The exact same \`clientMutationId\` that was provided in the mutation input, + unchanged and unused. May be used by a client to track mutations. + """ + clientMutationId: String + + """ + Our root query field type. Allows us to run any query from our mutation payload. + """ + query: Query + """ + Upload content to this signed URL. + """ + uploadUrl: String! + } + extend type Mutation { """ - Get a signed URL for uploading files. It will expire in 60 seconds. + Get a signed URL for uploading files. It will expire in 5 minutes. """ - createUploadUrl(contentType: String): String! + createUploadUrl( + """ + The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. + """ + input: CreateUploadUrlInput! + ): CreateUploadUrlPayload } `, resolvers: { Mutation: { - async createUploadUrl(_query, args, _context, _resolveInfo) { - const { contentType } = args; + async createUploadUrl( + _query, + args, + context: OurGraphQLContext, + _resolveInfo + ) { + const { rootPgPool } = context; + const { + rows: [user], + } = await rootPgPool.query( + `select username from app_public.users where id = app_public.current_user_id()` + ); + const username: string = user.username; + + const contentType = + ALLOWED_CONTENT_TYPE_ENUM_MAPPING[args.input.contentType]; const s3 = new aws.S3({ region: awsRegion, signatureVersion: "v4", @@ -23,12 +114,17 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ const params = { Bucket: uploadBucket, ContentType: contentType, - Key: uuidv4(), // randomly generated file name - Expires: 60, // signed URL will expire in 60 seconds + // randomly generated filename, nested under username directory + Key: `${username}/${uuidv4()}`, + Expires: 300, // signed URL will expire in 5 minutes ACL: "public-read", // uploaded file will be publicly readable }; const signedUrl = await s3.getSignedUrlPromise("putObject", params); - return signedUrl; + return { + clientMutationId: args.clientMutationId, + // what to do about `query` ? + uploadUrl: signedUrl, + }; }, }, }, From c9378b314432a35596b26cb4862aa4a270a3827d Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 18 Apr 2020 15:11:09 +0200 Subject: [PATCH 03/13] update schema.graphql --- data/schema.graphql | 826 ++++++++++++++------------------------------ 1 file changed, 256 insertions(+), 570 deletions(-) diff --git a/data/schema.graphql b/data/schema.graphql index 9e0c2015..d449a0ca 100644 --- a/data/schema.graphql +++ b/data/schema.graphql @@ -1,6 +1,4 @@ -""" -All input for the `acceptInvitationToOrganization` mutation. -""" +"""All input for the `acceptInvitationToOrganization` mutation.""" input AcceptInvitationToOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -11,9 +9,7 @@ input AcceptInvitationToOrganizationInput { invitationId: UUID! } -""" -The output of our `acceptInvitationToOrganization` mutation. -""" +"""The output of our `acceptInvitationToOrganization` mutation.""" type AcceptInvitationToOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -27,9 +23,34 @@ type AcceptInvitationToOrganizationPayload { query: Query } -""" -All input for the `changePassword` mutation. -""" +"""The set of content types that we allow users to upload.""" +enum AllowedUploadContentType { + """image/apng""" + IMAGE_APNG + + """image/bmp""" + IMAGE_BMP + + """image/gif""" + IMAGE_GIF + + """image/jpeg""" + IMAGE_JPEG + + """image/png""" + IMAGE_PNG + + """image/svg+xml""" + IMAGE_SVG_XML + + """image/tiff""" + IMAGE_TIFF + + """image/webp""" + IMAGE_WEBP +} + +"""All input for the `changePassword` mutation.""" input ChangePasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -40,9 +61,7 @@ input ChangePasswordInput { oldPassword: String! } -""" -The output of our `changePassword` mutation. -""" +"""The output of our `changePassword` mutation.""" type ChangePasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -57,9 +76,7 @@ type ChangePasswordPayload { success: Boolean } -""" -All input for the `confirmAccountDeletion` mutation. -""" +"""All input for the `confirmAccountDeletion` mutation.""" input ConfirmAccountDeletionInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -69,9 +86,7 @@ input ConfirmAccountDeletionInput { token: String! } -""" -The output of our `confirmAccountDeletion` mutation. -""" +"""The output of our `confirmAccountDeletion` mutation.""" type ConfirmAccountDeletionPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -86,9 +101,7 @@ type ConfirmAccountDeletionPayload { success: Boolean } -""" -All input for the `createOrganization` mutation. -""" +"""All input for the `createOrganization` mutation.""" input CreateOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -99,9 +112,7 @@ input CreateOrganizationInput { slug: String! } -""" -The output of our `createOrganization` mutation. -""" +"""The output of our `createOrganization` mutation.""" type CreateOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -110,13 +121,9 @@ type CreateOrganizationPayload { clientMutationId: String organization: Organization - """ - An edge for our `Organization`. May be used by Relay 1. - """ + """An edge for our `Organization`. May be used by Relay 1.""" organizationEdge( - """ - The method to use when ordering `Organization`. - """ + """The method to use when ordering `Organization`.""" orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -126,10 +133,8 @@ type CreateOrganizationPayload { query: Query } -""" -All input for the create `UserEmail` mutation. -""" -input CreateUserEmailInput { +"""All input for the `createUploadUrl` mutation.""" +input CreateUploadUrlInput { """ An arbitrary string value with no semantic meaning. Will be included in the payload verbatim. May be used to track mutations by the client. @@ -137,15 +142,15 @@ input CreateUserEmailInput { clientMutationId: String """ - The `UserEmail` to be created by this mutation. + You must provide the content type (or MIME type) of the content you intend + to upload. For further information about content types, see + https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types """ - userEmail: UserEmailInput! + contentType: AllowedUploadContentType! } -""" -The output of our create `UserEmail` mutation. -""" -type CreateUserEmailPayload { +"""The output of our `createUploadUrl mutation.""" +type CreateUploadUrlPayload { """ The exact same `clientMutationId` that was provided in the mutation input, unchanged and unused. May be used by a client to track mutations. @@ -157,30 +162,49 @@ type CreateUserEmailPayload { """ query: Query + """Upload content to this signed URL.""" + uploadUrl: String! +} + +"""All input for the create `UserEmail` mutation.""" +input CreateUserEmailInput { """ - Reads a single `User` that is related to this `UserEmail`. + An arbitrary string value with no semantic meaning. Will be included in the + payload verbatim. May be used to track mutations by the client. """ - user: User + clientMutationId: String + + """The `UserEmail` to be created by this mutation.""" + userEmail: UserEmailInput! +} +"""The output of our create `UserEmail` mutation.""" +type CreateUserEmailPayload { """ - The `UserEmail` that was created by this mutation. + The exact same `clientMutationId` that was provided in the mutation input, + unchanged and unused. May be used by a client to track mutations. """ - userEmail: UserEmail + clientMutationId: String """ - An edge for our `UserEmail`. May be used by Relay 1. + Our root query field type. Allows us to run any query from our mutation payload. """ + query: Query + + """Reads a single `User` that is related to this `UserEmail`.""" + user: User + + """The `UserEmail` that was created by this mutation.""" + userEmail: UserEmail + + """An edge for our `UserEmail`. May be used by Relay 1.""" userEmailEdge( - """ - The method to use when ordering `UserEmail`. - """ + """The method to use when ordering `UserEmail`.""" orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } -""" -A location in a connection that can be used for resuming pagination. -""" +"""A location in a connection that can be used for resuming pagination.""" scalar Cursor """ @@ -189,9 +213,7 @@ A point in time as described by the [ISO """ scalar Datetime -""" -All input for the `deleteOrganization` mutation. -""" +"""All input for the `deleteOrganization` mutation.""" input DeleteOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -201,9 +223,7 @@ input DeleteOrganizationInput { organizationId: UUID! } -""" -The output of our `deleteOrganization` mutation. -""" +"""The output of our `deleteOrganization` mutation.""" type DeleteOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -217,9 +237,7 @@ type DeleteOrganizationPayload { query: Query } -""" -All input for the `deleteUserAuthentication` mutation. -""" +"""All input for the `deleteUserAuthentication` mutation.""" input DeleteUserAuthenticationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -229,9 +247,7 @@ input DeleteUserAuthenticationInput { id: UUID! } -""" -The output of our delete `UserAuthentication` mutation. -""" +"""The output of our delete `UserAuthentication` mutation.""" type DeleteUserAuthenticationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -245,20 +261,14 @@ type DeleteUserAuthenticationPayload { """ query: Query - """ - Reads a single `User` that is related to this `UserAuthentication`. - """ + """Reads a single `User` that is related to this `UserAuthentication`.""" user: User - """ - The `UserAuthentication` that was deleted by this mutation. - """ + """The `UserAuthentication` that was deleted by this mutation.""" userAuthentication: UserAuthentication } -""" -All input for the `deleteUserEmail` mutation. -""" +"""All input for the `deleteUserEmail` mutation.""" input DeleteUserEmailInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -268,9 +278,7 @@ input DeleteUserEmailInput { id: UUID! } -""" -The output of our delete `UserEmail` mutation. -""" +"""The output of our delete `UserEmail` mutation.""" type DeleteUserEmailPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -284,30 +292,20 @@ type DeleteUserEmailPayload { """ query: Query - """ - Reads a single `User` that is related to this `UserEmail`. - """ + """Reads a single `User` that is related to this `UserEmail`.""" user: User - """ - The `UserEmail` that was deleted by this mutation. - """ + """The `UserEmail` that was deleted by this mutation.""" userEmail: UserEmail - """ - An edge for our `UserEmail`. May be used by Relay 1. - """ + """An edge for our `UserEmail`. May be used by Relay 1.""" userEmailEdge( - """ - The method to use when ordering `UserEmail`. - """ + """The method to use when ordering `UserEmail`.""" orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } -""" -All input for the `forgotPassword` mutation. -""" +"""All input for the `forgotPassword` mutation.""" input ForgotPasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -317,9 +315,7 @@ input ForgotPasswordInput { email: String! } -""" -The output of our `forgotPassword` mutation. -""" +"""The output of our `forgotPassword` mutation.""" type ForgotPasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -333,9 +329,7 @@ type ForgotPasswordPayload { query: Query } -""" -All input for the `inviteToOrganization` mutation. -""" +"""All input for the `inviteToOrganization` mutation.""" input InviteToOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -347,9 +341,7 @@ input InviteToOrganizationInput { username: String } -""" -The output of our `inviteToOrganization` mutation. -""" +"""The output of our `inviteToOrganization` mutation.""" type InviteToOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -376,9 +368,7 @@ type LogoutPayload { success: Boolean } -""" -All input for the `makeEmailPrimary` mutation. -""" +"""All input for the `makeEmailPrimary` mutation.""" input MakeEmailPrimaryInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -388,9 +378,7 @@ input MakeEmailPrimaryInput { emailId: UUID! } -""" -The output of our `makeEmailPrimary` mutation. -""" +"""The output of our `makeEmailPrimary` mutation.""" type MakeEmailPrimaryPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -403,19 +391,13 @@ type MakeEmailPrimaryPayload { """ query: Query - """ - Reads a single `User` that is related to this `UserEmail`. - """ + """Reads a single `User` that is related to this `UserEmail`.""" user: User userEmail: UserEmail - """ - An edge for our `UserEmail`. May be used by Relay 1. - """ + """An edge for our `UserEmail`. May be used by Relay 1.""" userEmailEdge( - """ - The method to use when ordering `UserEmail`. - """ + """The method to use when ordering `UserEmail`.""" orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsEdge } @@ -436,9 +418,7 @@ type Mutation { input: AcceptInvitationToOrganizationInput! ): AcceptInvitationToOrganizationPayload - """ - Enter your old password and a new password to change your password. - """ + """Enter your old password and a new password to change your password.""" changePassword( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -471,9 +451,15 @@ type Mutation { input: CreateOrganizationInput! ): CreateOrganizationPayload - """ - Creates a single `UserEmail`. - """ + """Get a signed URL for uploading files. It will expire in 5 minutes.""" + createUploadUrl( + """ + The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. + """ + input: CreateUploadUrlInput! + ): CreateUploadUrlPayload + + """Creates a single `UserEmail`.""" createUserEmail( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -491,9 +477,7 @@ type Mutation { input: DeleteOrganizationInput! ): DeleteOrganizationPayload - """ - Deletes a single `UserAuthentication` using a unique key. - """ + """Deletes a single `UserAuthentication` using a unique key.""" deleteUserAuthentication( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -501,9 +485,7 @@ type Mutation { input: DeleteUserAuthenticationInput! ): DeleteUserAuthenticationPayload - """ - Deletes a single `UserEmail` using a unique key. - """ + """Deletes a single `UserEmail` using a unique key.""" deleteUserEmail( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -583,9 +565,7 @@ type Mutation { input: RemoveFromOrganizationInput! ): RemoveFromOrganizationPayload - """ - Begin the account deletion flow by requesting the confirmation email - """ + """Begin the account deletion flow by requesting the confirmation email""" requestAccountDeletion( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -639,9 +619,7 @@ type Mutation { input: TransferOrganizationOwnershipInput! ): TransferOrganizationOwnershipPayload - """ - Updates a single `Organization` using a unique key and a patch. - """ + """Updates a single `Organization` using a unique key and a patch.""" updateOrganization( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -649,9 +627,7 @@ type Mutation { input: UpdateOrganizationInput! ): UpdateOrganizationPayload - """ - Updates a single `User` using a unique key and a patch. - """ + """Updates a single `User` using a unique key and a patch.""" updateUser( """ The exclusive input argument for this mutation. An object type, make sure to see documentation for this object’s fields. @@ -682,14 +658,10 @@ type Organization { Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -697,14 +669,10 @@ type Organization { """ condition: OrganizationInvitationCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -713,9 +681,7 @@ type Organization { """ offset: Int - """ - The method to use when ordering `OrganizationInvitation`. - """ + """The method to use when ordering `OrganizationInvitation`.""" orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection! @@ -723,14 +689,10 @@ type Organization { Reads and enables pagination through a set of `OrganizationMembership`. """ organizationMemberships( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -738,14 +700,10 @@ type Organization { """ condition: OrganizationMembershipCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -754,9 +712,7 @@ type Organization { """ offset: Int - """ - The method to use when ordering `OrganizationMembership`. - """ + """The method to use when ordering `OrganizationMembership`.""" orderBy: [OrganizationMembershipsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationMembershipsConnection! slug: String! @@ -767,14 +723,10 @@ A condition to be used against `Organization` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationCondition { - """ - Checks for equality with the object’s `id` field. - """ + """Checks for equality with the object’s `id` field.""" id: UUID - """ - Checks for equality with the object’s `slug` field. - """ + """Checks for equality with the object’s `slug` field.""" slug: String } @@ -801,39 +753,27 @@ A condition to be used against `OrganizationInvitation` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationInvitationCondition { - """ - Checks for equality with the object’s `id` field. - """ + """Checks for equality with the object’s `id` field.""" id: UUID - """ - Checks for equality with the object’s `organizationId` field. - """ + """Checks for equality with the object’s `organizationId` field.""" organizationId: UUID - """ - Checks for equality with the object’s `userId` field. - """ + """Checks for equality with the object’s `userId` field.""" userId: UUID } -""" -A connection to a list of `OrganizationInvitation` values. -""" +"""A connection to a list of `OrganizationInvitation` values.""" type OrganizationInvitationsConnection { """ A list of edges which contains the `OrganizationInvitation` and cursor to aid in pagination. """ edges: [OrganizationInvitationsEdge!]! - """ - A list of `OrganizationInvitation` objects. - """ + """A list of `OrganizationInvitation` objects.""" nodes: [OrganizationInvitation!]! - """ - Information to aid in pagination. - """ + """Information to aid in pagination.""" pageInfo: PageInfo! """ @@ -842,24 +782,16 @@ type OrganizationInvitationsConnection { totalCount: Int! } -""" -A `OrganizationInvitation` edge in the connection. -""" +"""A `OrganizationInvitation` edge in the connection.""" type OrganizationInvitationsEdge { - """ - A cursor for use in pagination. - """ + """A cursor for use in pagination.""" cursor: Cursor - """ - The `OrganizationInvitation` at the end of the edge. - """ + """The `OrganizationInvitation` at the end of the edge.""" node: OrganizationInvitation! } -""" -Methods to use when ordering `OrganizationInvitation`. -""" +"""Methods to use when ordering `OrganizationInvitation`.""" enum OrganizationInvitationsOrderBy { ID_ASC ID_DESC @@ -896,39 +828,27 @@ A condition to be used against `OrganizationMembership` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input OrganizationMembershipCondition { - """ - Checks for equality with the object’s `id` field. - """ + """Checks for equality with the object’s `id` field.""" id: UUID - """ - Checks for equality with the object’s `organizationId` field. - """ + """Checks for equality with the object’s `organizationId` field.""" organizationId: UUID - """ - Checks for equality with the object’s `userId` field. - """ + """Checks for equality with the object’s `userId` field.""" userId: UUID } -""" -A connection to a list of `OrganizationMembership` values. -""" +"""A connection to a list of `OrganizationMembership` values.""" type OrganizationMembershipsConnection { """ A list of edges which contains the `OrganizationMembership` and cursor to aid in pagination. """ edges: [OrganizationMembershipsEdge!]! - """ - A list of `OrganizationMembership` objects. - """ + """A list of `OrganizationMembership` objects.""" nodes: [OrganizationMembership!]! - """ - Information to aid in pagination. - """ + """Information to aid in pagination.""" pageInfo: PageInfo! """ @@ -937,24 +857,16 @@ type OrganizationMembershipsConnection { totalCount: Int! } -""" -A `OrganizationMembership` edge in the connection. -""" +"""A `OrganizationMembership` edge in the connection.""" type OrganizationMembershipsEdge { - """ - A cursor for use in pagination. - """ + """A cursor for use in pagination.""" cursor: Cursor - """ - The `OrganizationMembership` at the end of the edge. - """ + """The `OrganizationMembership` at the end of the edge.""" node: OrganizationMembership! } -""" -Methods to use when ordering `OrganizationMembership`. -""" +"""Methods to use when ordering `OrganizationMembership`.""" enum OrganizationMembershipsOrderBy { ID_ASC ID_DESC @@ -977,49 +889,33 @@ input OrganizationPatch { slug: String } -""" -A connection to a list of `Organization` values. -""" +"""A connection to a list of `Organization` values.""" type OrganizationsConnection { """ A list of edges which contains the `Organization` and cursor to aid in pagination. """ edges: [OrganizationsEdge!]! - """ - A list of `Organization` objects. - """ + """A list of `Organization` objects.""" nodes: [Organization!]! - """ - Information to aid in pagination. - """ + """Information to aid in pagination.""" pageInfo: PageInfo! - """ - The count of *all* `Organization` you could get from the connection. - """ + """The count of *all* `Organization` you could get from the connection.""" totalCount: Int! } -""" -A `Organization` edge in the connection. -""" +"""A `Organization` edge in the connection.""" type OrganizationsEdge { - """ - A cursor for use in pagination. - """ + """A cursor for use in pagination.""" cursor: Cursor - """ - The `Organization` at the end of the edge. - """ + """The `Organization` at the end of the edge.""" node: Organization! } -""" -Methods to use when ordering `Organization`. -""" +"""Methods to use when ordering `Organization`.""" enum OrganizationsOrderBy { ID_ASC ID_DESC @@ -1030,38 +926,24 @@ enum OrganizationsOrderBy { SLUG_DESC } -""" -Information about pagination in a connection. -""" +"""Information about pagination in a connection.""" type PageInfo { - """ - When paginating forwards, the cursor to continue. - """ + """When paginating forwards, the cursor to continue.""" endCursor: Cursor - """ - When paginating forwards, are there more items? - """ + """When paginating forwards, are there more items?""" hasNextPage: Boolean! - """ - When paginating backwards, are there more items? - """ + """When paginating backwards, are there more items?""" hasPreviousPage: Boolean! - """ - When paginating backwards, the cursor to continue. - """ + """When paginating backwards, the cursor to continue.""" startCursor: Cursor } -""" -The root query type which gives access points into the data universe. -""" +"""The root query type which gives access points into the data universe.""" type Query { - """ - The currently logged in user (or null if not logged in). - """ + """The currently logged in user (or null if not logged in).""" currentUser: User organization(id: UUID!): Organization organizationBySlug(slug: String!): Organization @@ -1077,14 +959,10 @@ type Query { Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -1092,14 +970,10 @@ type Query { """ condition: OrganizationInvitationCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -1108,25 +982,17 @@ type Query { """ offset: Int - """ - The method to use when ordering `OrganizationInvitation`. - """ + """The method to use when ordering `OrganizationInvitation`.""" orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection organizationMembership(id: UUID!): OrganizationMembership - """ - Reads and enables pagination through a set of `Organization`. - """ + """Reads and enables pagination through a set of `Organization`.""" organizations( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -1134,14 +1000,10 @@ type Query { """ condition: OrganizationCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -1150,9 +1012,7 @@ type Query { """ offset: Int - """ - The method to use when ordering `Organization`. - """ + """The method to use when ordering `Organization`.""" orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsConnection user(id: UUID!): User @@ -1173,9 +1033,7 @@ type RegisterPayload { user: User! } -""" -All input for the `removeFromOrganization` mutation. -""" +"""All input for the `removeFromOrganization` mutation.""" input RemoveFromOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1186,9 +1044,7 @@ input RemoveFromOrganizationInput { userId: UUID! } -""" -The output of our `removeFromOrganization` mutation. -""" +"""The output of our `removeFromOrganization` mutation.""" type RemoveFromOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1202,9 +1058,7 @@ type RemoveFromOrganizationPayload { query: Query } -""" -All input for the `requestAccountDeletion` mutation. -""" +"""All input for the `requestAccountDeletion` mutation.""" input RequestAccountDeletionInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1213,9 +1067,7 @@ input RequestAccountDeletionInput { clientMutationId: String } -""" -The output of our `requestAccountDeletion` mutation. -""" +"""The output of our `requestAccountDeletion` mutation.""" type RequestAccountDeletionPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1230,9 +1082,7 @@ type RequestAccountDeletionPayload { success: Boolean } -""" -All input for the `resendEmailVerificationCode` mutation. -""" +"""All input for the `resendEmailVerificationCode` mutation.""" input ResendEmailVerificationCodeInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1242,9 +1092,7 @@ input ResendEmailVerificationCodeInput { emailId: UUID! } -""" -The output of our `resendEmailVerificationCode` mutation. -""" +"""The output of our `resendEmailVerificationCode` mutation.""" type ResendEmailVerificationCodePayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1259,9 +1107,7 @@ type ResendEmailVerificationCodePayload { success: Boolean } -""" -All input for the `resetPassword` mutation. -""" +"""All input for the `resetPassword` mutation.""" input ResetPasswordInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1273,9 +1119,7 @@ input ResetPasswordInput { userId: UUID! } -""" -The output of our `resetPassword` mutation. -""" +"""The output of our `resetPassword` mutation.""" type ResetPasswordPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1294,15 +1138,11 @@ type ResetPasswordPayload { The root subscription type: contains realtime events you can subscribe to with the `subscription` operation. """ type Subscription { - """ - Triggered when the logged in user's record is updated in some way. - """ + """Triggered when the logged in user's record is updated in some way.""" currentUserUpdated: UserSubscriptionPayload } -""" -All input for the `transferOrganizationBillingContact` mutation. -""" +"""All input for the `transferOrganizationBillingContact` mutation.""" input TransferOrganizationBillingContactInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1313,9 +1153,7 @@ input TransferOrganizationBillingContactInput { userId: UUID! } -""" -The output of our `transferOrganizationBillingContact` mutation. -""" +"""The output of our `transferOrganizationBillingContact` mutation.""" type TransferOrganizationBillingContactPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1324,13 +1162,9 @@ type TransferOrganizationBillingContactPayload { clientMutationId: String organization: Organization - """ - An edge for our `Organization`. May be used by Relay 1. - """ + """An edge for our `Organization`. May be used by Relay 1.""" organizationEdge( - """ - The method to use when ordering `Organization`. - """ + """The method to use when ordering `Organization`.""" orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1340,9 +1174,7 @@ type TransferOrganizationBillingContactPayload { query: Query } -""" -All input for the `transferOrganizationOwnership` mutation. -""" +"""All input for the `transferOrganizationOwnership` mutation.""" input TransferOrganizationOwnershipInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1353,9 +1185,7 @@ input TransferOrganizationOwnershipInput { userId: UUID! } -""" -The output of our `transferOrganizationOwnership` mutation. -""" +"""The output of our `transferOrganizationOwnership` mutation.""" type TransferOrganizationOwnershipPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1364,13 +1194,9 @@ type TransferOrganizationOwnershipPayload { clientMutationId: String organization: Organization - """ - An edge for our `Organization`. May be used by Relay 1. - """ + """An edge for our `Organization`. May be used by Relay 1.""" organizationEdge( - """ - The method to use when ordering `Organization`. - """ + """The method to use when ordering `Organization`.""" orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1380,9 +1206,7 @@ type TransferOrganizationOwnershipPayload { query: Query } -""" -All input for the `updateOrganization` mutation. -""" +"""All input for the `updateOrganization` mutation.""" input UpdateOrganizationInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1397,9 +1221,7 @@ input UpdateOrganizationInput { patch: OrganizationPatch! } -""" -The output of our update `Organization` mutation. -""" +"""The output of our update `Organization` mutation.""" type UpdateOrganizationPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1407,18 +1229,12 @@ type UpdateOrganizationPayload { """ clientMutationId: String - """ - The `Organization` that was updated by this mutation. - """ + """The `Organization` that was updated by this mutation.""" organization: Organization - """ - An edge for our `Organization`. May be used by Relay 1. - """ + """An edge for our `Organization`. May be used by Relay 1.""" organizationEdge( - """ - The method to use when ordering `Organization`. - """ + """The method to use when ordering `Organization`.""" orderBy: [OrganizationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationsEdge @@ -1428,9 +1244,7 @@ type UpdateOrganizationPayload { query: Query } -""" -All input for the `updateUser` mutation. -""" +"""All input for the `updateUser` mutation.""" input UpdateUserInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1438,9 +1252,7 @@ input UpdateUserInput { """ clientMutationId: String - """ - Unique identifier for the user. - """ + """Unique identifier for the user.""" id: UUID! """ @@ -1449,9 +1261,7 @@ input UpdateUserInput { patch: UserPatch! } -""" -The output of our update `User` mutation. -""" +"""The output of our update `User` mutation.""" type UpdateUserPayload { """ The exact same `clientMutationId` that was provided in the mutation input, @@ -1464,61 +1274,41 @@ type UpdateUserPayload { """ query: Query - """ - The `User` that was updated by this mutation. - """ + """The `User` that was updated by this mutation.""" user: User - """ - An edge for our `User`. May be used by Relay 1. - """ + """An edge for our `User`. May be used by Relay 1.""" userEdge( - """ - The method to use when ordering `User`. - """ + """The method to use when ordering `User`.""" orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC] ): UsersEdge } -""" -A user who can log in to the application. -""" +"""A user who can log in to the application.""" type User { - """ - Optional avatar URL. - """ + """Optional avatar URL.""" avatarUrl: String createdAt: Datetime! hasPassword: Boolean - """ - Unique identifier for the user. - """ + """Unique identifier for the user.""" id: UUID! - """ - If true, the user has elevated privileges. - """ + """If true, the user has elevated privileges.""" isAdmin: Boolean! isVerified: Boolean! - """ - Public-facing name (or pseudonym) of the user. - """ + """Public-facing name (or pseudonym) of the user.""" name: String """ Reads and enables pagination through a set of `OrganizationInvitation`. """ organizationInvitations( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -1526,14 +1316,10 @@ type User { """ condition: OrganizationInvitationCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -1542,9 +1328,7 @@ type User { """ offset: Int - """ - The method to use when ordering `OrganizationInvitation`. - """ + """The method to use when ordering `OrganizationInvitation`.""" orderBy: [OrganizationInvitationsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationInvitationsConnection! @@ -1552,14 +1336,10 @@ type User { Reads and enables pagination through a set of `OrganizationMembership`. """ organizationMemberships( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -1567,14 +1347,10 @@ type User { """ condition: OrganizationMembershipCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -1583,50 +1359,34 @@ type User { """ offset: Int - """ - The method to use when ordering `OrganizationMembership`. - """ + """The method to use when ordering `OrganizationMembership`.""" orderBy: [OrganizationMembershipsOrderBy!] = [PRIMARY_KEY_ASC] ): OrganizationMembershipsConnection! updatedAt: Datetime! - """ - Reads and enables pagination through a set of `UserAuthentication`. - """ + """Reads and enables pagination through a set of `UserAuthentication`.""" userAuthenticationsList( """ A condition to be used in determining which values should be returned by the collection. """ condition: UserAuthenticationCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Skip the first `n` values. - """ + """Skip the first `n` values.""" offset: Int - """ - The method to use when ordering `UserAuthentication`. - """ + """The method to use when ordering `UserAuthentication`.""" orderBy: [UserAuthenticationsOrderBy!] ): [UserAuthentication!]! - """ - Reads and enables pagination through a set of `UserEmail`. - """ + """Reads and enables pagination through a set of `UserEmail`.""" userEmails( - """ - Read all values in the set after (below) this cursor. - """ + """Read all values in the set after (below) this cursor.""" after: Cursor - """ - Read all values in the set before (above) this cursor. - """ + """Read all values in the set before (above) this cursor.""" before: Cursor """ @@ -1634,14 +1394,10 @@ type User { """ condition: UserEmailCondition - """ - Only read the first `n` values of the set. - """ + """Only read the first `n` values of the set.""" first: Int - """ - Only read the last `n` values of the set. - """ + """Only read the last `n` values of the set.""" last: Int """ @@ -1650,15 +1406,11 @@ type User { """ offset: Int - """ - The method to use when ordering `UserEmail`. - """ + """The method to use when ordering `UserEmail`.""" orderBy: [UserEmailsOrderBy!] = [PRIMARY_KEY_ASC] ): UserEmailsConnection! - """ - Public-facing username (or 'handle') of the user. - """ + """Public-facing username (or 'handle') of the user.""" username: String! } @@ -1669,20 +1421,14 @@ type UserAuthentication { createdAt: Datetime! id: UUID! - """ - A unique identifier for the user within the login service. - """ + """A unique identifier for the user within the login service.""" identifier: String! - """ - The login service used, e.g. `twitter` or `github`. - """ + """The login service used, e.g. `twitter` or `github`.""" service: String! updatedAt: Datetime! - """ - Reads a single `User` that is related to this `UserAuthentication`. - """ + """Reads a single `User` that is related to this `UserAuthentication`.""" user: User userId: UUID! } @@ -1692,25 +1438,17 @@ A condition to be used against `UserAuthentication` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input UserAuthenticationCondition { - """ - Checks for equality with the object’s `id` field. - """ + """Checks for equality with the object’s `id` field.""" id: UUID - """ - Checks for equality with the object’s `service` field. - """ + """Checks for equality with the object’s `service` field.""" service: String - """ - Checks for equality with the object’s `userId` field. - """ + """Checks for equality with the object’s `userId` field.""" userId: UUID } -""" -Methods to use when ordering `UserAuthentication`. -""" +"""Methods to use when ordering `UserAuthentication`.""" enum UserAuthenticationsOrderBy { ID_ASC ID_DESC @@ -1723,15 +1461,11 @@ enum UserAuthenticationsOrderBy { USER_ID_DESC } -""" -Information about a user's email address. -""" +"""Information about a user's email address.""" type UserEmail { createdAt: Datetime! - """ - The users email address, in `a@b.c` format. - """ + """The users email address, in `a@b.c` format.""" email: String! id: UUID! isPrimary: Boolean! @@ -1743,9 +1477,7 @@ type UserEmail { isVerified: Boolean! updatedAt: Datetime! - """ - Reads a single `User` that is related to this `UserEmail`. - """ + """Reads a single `User` that is related to this `UserEmail`.""" user: User userId: UUID! } @@ -1755,75 +1487,49 @@ A condition to be used against `UserEmail` object types. All fields are tested for equality and combined with a logical ‘and.’ """ input UserEmailCondition { - """ - Checks for equality with the object’s `id` field. - """ + """Checks for equality with the object’s `id` field.""" id: UUID - """ - Checks for equality with the object’s `isPrimary` field. - """ + """Checks for equality with the object’s `isPrimary` field.""" isPrimary: Boolean - """ - Checks for equality with the object’s `userId` field. - """ + """Checks for equality with the object’s `userId` field.""" userId: UUID } -""" -An input for mutations affecting `UserEmail` -""" +"""An input for mutations affecting `UserEmail`""" input UserEmailInput { - """ - The users email address, in `a@b.c` format. - """ + """The users email address, in `a@b.c` format.""" email: String! } -""" -A connection to a list of `UserEmail` values. -""" +"""A connection to a list of `UserEmail` values.""" type UserEmailsConnection { """ A list of edges which contains the `UserEmail` and cursor to aid in pagination. """ edges: [UserEmailsEdge!]! - """ - A list of `UserEmail` objects. - """ + """A list of `UserEmail` objects.""" nodes: [UserEmail!]! - """ - Information to aid in pagination. - """ + """Information to aid in pagination.""" pageInfo: PageInfo! - """ - The count of *all* `UserEmail` you could get from the connection. - """ + """The count of *all* `UserEmail` you could get from the connection.""" totalCount: Int! } -""" -A `UserEmail` edge in the connection. -""" +"""A `UserEmail` edge in the connection.""" type UserEmailsEdge { - """ - A cursor for use in pagination. - """ + """A cursor for use in pagination.""" cursor: Cursor - """ - The `UserEmail` at the end of the edge. - """ + """The `UserEmail` at the end of the edge.""" node: UserEmail! } -""" -Methods to use when ordering `UserEmail`. -""" +"""Methods to use when ordering `UserEmail`.""" enum UserEmailsOrderBy { ID_ASC ID_DESC @@ -1836,44 +1542,28 @@ enum UserEmailsOrderBy { USER_ID_DESC } -""" -Represents an update to a `User`. Fields that are set will be updated. -""" +"""Represents an update to a `User`. Fields that are set will be updated.""" input UserPatch { - """ - Optional avatar URL. - """ + """Optional avatar URL.""" avatarUrl: String - """ - Public-facing name (or pseudonym) of the user. - """ + """Public-facing name (or pseudonym) of the user.""" name: String - """ - Public-facing username (or 'handle') of the user. - """ + """Public-facing username (or 'handle') of the user.""" username: String } -""" -A `User` edge in the connection. -""" +"""A `User` edge in the connection.""" type UsersEdge { - """ - A cursor for use in pagination. - """ + """A cursor for use in pagination.""" cursor: Cursor - """ - The `User` at the end of the edge. - """ + """The `User` at the end of the edge.""" node: User! } -""" -Methods to use when ordering `User`. -""" +"""Methods to use when ordering `User`.""" enum UsersOrderBy { ID_ASC ID_DESC @@ -1894,9 +1584,7 @@ A universally unique identifier as defined by [RFC 4122](https://tools.ietf.org/ """ scalar UUID -""" -All input for the `verifyEmail` mutation. -""" +"""All input for the `verifyEmail` mutation.""" input VerifyEmailInput { """ An arbitrary string value with no semantic meaning. Will be included in the @@ -1907,9 +1595,7 @@ input VerifyEmailInput { userEmailId: UUID! } -""" -The output of our `verifyEmail` mutation. -""" +"""The output of our `verifyEmail` mutation.""" type VerifyEmailPayload { """ The exact same `clientMutationId` that was provided in the mutation input, From 030909077118ab49f52eb62c0481bf3ad81932da Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 18 Apr 2020 15:35:09 +0200 Subject: [PATCH 04/13] use typescript enum --- .../src/plugins/CreateUploadUrlPlugin.ts | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index f39b0433..9e54ab0f 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -5,16 +5,21 @@ import uuidv4 from "uuid/v4"; import { OurGraphQLContext } from "../middleware/installPostGraphile"; -const ALLOWED_CONTENT_TYPE_ENUM_MAPPING = { - IMAGE_APNG: "image/apng", - IMAGE_BMP: "image/bmp", - IMAGE_GIF: "image/gif", - IMAGE_JPEG: "image/jpeg", - IMAGE_PNG: "image/png", - IMAGE_SVG_XML: "image/svg+xml", - IMAGE_TIFF: "image/tiff", - IMAGE_WEBP: "image/webp", -}; +enum AllowedUploadContentType { + IMAGE_APNG = "image/apng", + IMAGE_BMP = "image/bmp", + IMAGE_GIF = "image/gif", + IMAGE_JPEG = "image/jpeg", + IMAGE_PNG = "image/png", + IMAGE_SVG_XML = "image/svg+xml", + IMAGE_TIFF = "image/tiff", + IMAGE_WEBP = "image/webp", +} + +interface CreateUploadUrlInput { + clientMutationId?: string; + contentType: AllowedUploadContentType; +} const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ typeDefs: gql` @@ -93,11 +98,12 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ Mutation: { async createUploadUrl( _query, - args, + args: { input: CreateUploadUrlInput }, context: OurGraphQLContext, _resolveInfo ) { const { rootPgPool } = context; + const { input } = args; const { rows: [user], } = await rootPgPool.query( @@ -105,8 +111,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ ); const username: string = user.username; - const contentType = - ALLOWED_CONTENT_TYPE_ENUM_MAPPING[args.input.contentType]; + const contentType: string = AllowedUploadContentType[input.contentType]; const s3 = new aws.S3({ region: awsRegion, signatureVersion: "v4", @@ -121,8 +126,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ }; const signedUrl = await s3.getSignedUrlPromise("putObject", params); return { - clientMutationId: args.clientMutationId, - // what to do about `query` ? + clientMutationId: input.clientMutationId, uploadUrl: signedUrl, }; }, From f28ebc26fed69a60468f36ebe555a09bd5639ef3 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 18 Apr 2020 15:37:42 +0200 Subject: [PATCH 05/13] prettier --- @app/server/src/middleware/installPostGraphile.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/@app/server/src/middleware/installPostGraphile.ts b/@app/server/src/middleware/installPostGraphile.ts index 915b43ff..54ba5940 100644 --- a/@app/server/src/middleware/installPostGraphile.ts +++ b/@app/server/src/middleware/installPostGraphile.ts @@ -15,12 +15,12 @@ import { import { makePgSmartTagsFromFilePlugin } from "postgraphile/plugins"; import { getHttpServer, getWebsocketMiddlewares } from "../app"; +import CreateUploadUrlPlugin from "../plugins/CreateUploadUrlPlugin"; import OrdersPlugin from "../plugins/Orders"; import PassportLoginPlugin from "../plugins/PassportLoginPlugin"; import PrimaryKeyMutationsOnlyPlugin from "../plugins/PrimaryKeyMutationsOnlyPlugin"; import RemoveQueryQueryPlugin from "../plugins/RemoveQueryQueryPlugin"; import SubscriptionsPlugin from "../plugins/SubscriptionsPlugin"; -import CreateUploadUrlPlugin from "../plugins/CreateUploadUrlPlugin"; import handleErrors from "../utils/handleErrors"; import { getAuthPgPool, getRootPgPool } from "./installDatabasePools"; @@ -262,7 +262,7 @@ export function getPostGraphileOptions({ // Use this to tell Passport.js we're logged in login: (user: any) => new Promise((resolve, reject) => { - req.login(user, err => (err ? reject(err) : resolve())); + req.login(user, (err) => (err ? reject(err) : resolve())); }), logout: () => { From 76807dc8f081e239be2a619902f30c7b7e291184 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 18 Apr 2020 15:41:04 +0200 Subject: [PATCH 06/13] missed a backtick --- @app/server/src/plugins/CreateUploadUrlPlugin.ts | 2 +- data/schema.graphql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index 9e54ab0f..a6178619 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -63,7 +63,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ } """ - The output of our \`createUploadUrl mutation. + The output of our \`createUploadUrl\` mutation. """ type CreateUploadUrlPayload { """ diff --git a/data/schema.graphql b/data/schema.graphql index d449a0ca..00ec324a 100644 --- a/data/schema.graphql +++ b/data/schema.graphql @@ -149,7 +149,7 @@ input CreateUploadUrlInput { contentType: AllowedUploadContentType! } -"""The output of our `createUploadUrl mutation.""" +"""The output of our `createUploadUrl` mutation.""" type CreateUploadUrlPayload { """ The exact same `clientMutationId` that was provided in the mutation input, From 62c7e83b8c8dd024588ce9b291917e1721714507 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 20 Apr 2020 11:40:47 +0200 Subject: [PATCH 07/13] Add dependency on aws-sdk to server/package.json --- @app/server/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/@app/server/package.json b/@app/server/package.json index bbc84e51..b5e6dbb8 100644 --- a/@app/server/package.json +++ b/@app/server/package.json @@ -24,6 +24,7 @@ "@types/pg": "^7.14.1", "@types/redis": "^2.8.18", "@types/uuid": "^7.0.2", + "aws-sdk": "^2.645.0", "body-parser": "^1.19.0", "chalk": "^4.0.0", "connect-pg-simple": "^6.1.0", From 8626b2f98f6f093b7075892550d7b060d8d7a097 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 20 Apr 2020 12:47:31 +0200 Subject: [PATCH 08/13] respond to review comments --- .../src/plugins/CreateUploadUrlPlugin.ts | 92 +++++++++++++++---- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index a6178619..66d6ceae 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -1,6 +1,7 @@ import { awsRegion, uploadBucket } from "@app/config"; import * as aws from "aws-sdk"; import { gql, makeExtendSchemaPlugin } from "graphile-utils"; +import { Pool } from "pg"; import uuidv4 from "uuid/v4"; import { OurGraphQLContext } from "../middleware/installPostGraphile"; @@ -21,34 +22,79 @@ interface CreateUploadUrlInput { contentType: AllowedUploadContentType; } +/** The minimal set of information that this plugin needs to know about users. */ +interface User { + id: string; + isVerified: boolean; +} + +async function getCurrentUser(pool: Pool): Promise { + await pool.query("SAVEPOINT"); + try { + const { + rows: [row], + } = await pool.query( + "select id, is_verified from app_public.users where id = app_public.current_user_id()" + ); + if (!row) { + return null; + } + return { + id: row.id, + isVerified: row.is_verified, + }; + } catch (err) { + await pool.query("ROLLBACK TO SAVEPOINT"); + throw err; + } finally { + await pool.query("RELEASE SAVEPOINT"); + } +} + const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ typeDefs: gql` """ The set of content types that we allow users to upload. """ enum AllowedUploadContentType { - "image/apng" + """ + image/apng + """ IMAGE_APNG - "image/bmp" + """ + image/bmp + """ IMAGE_BMP - "image/gif" + """ + image/gif + """ IMAGE_GIF - "image/jpeg" + """ + image/jpeg + """ IMAGE_JPEG - "image/png" + """ + image/png + """ IMAGE_PNG - "image/svg+xml" + """ + image/svg+xml + """ IMAGE_SVG_XML - "image/tiff" + """ + image/tiff + """ IMAGE_TIFF - "image/webp" + """ + image/webp + """ IMAGE_WEBP } """ All input for the \`createUploadUrl\` mutation. """ - input CreateUploadUrlInput { + input CreateUploadUrlInput @scope(isMutationInput: true) { """ An arbitrary string value with no semantic meaning. Will be included in the payload verbatim. May be used to track mutations by the client. @@ -65,7 +111,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ """ The output of our \`createUploadUrl\` mutation. """ - type CreateUploadUrlPayload { + type CreateUploadUrlPayload @scope(isMutationPayload: true) { """ The exact same \`clientMutationId\` that was provided in the mutation input, unchanged and unused. May be used by a client to track mutations. @@ -102,15 +148,23 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ context: OurGraphQLContext, _resolveInfo ) { - const { rootPgPool } = context; - const { input } = args; - const { - rows: [user], - } = await rootPgPool.query( - `select username from app_public.users where id = app_public.current_user_id()` - ); - const username: string = user.username; + const user = await getCurrentUser(context.rootPgPool); + if (!user) { + const err = new Error("Login required"); + // @ts-ignore + err.code = "LOGIN"; + throw err; + } + + if (!user.isVerified) { + const err = new Error("Only verified users may upload files"); + // @ts-ignore + err.code = "DNIED"; + throw err; + } + + const { input } = args; const contentType: string = AllowedUploadContentType[input.contentType]; const s3 = new aws.S3({ region: awsRegion, @@ -120,7 +174,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ Bucket: uploadBucket, ContentType: contentType, // randomly generated filename, nested under username directory - Key: `${username}/${uuidv4()}`, + Key: `${user.id}/${uuidv4()}`, Expires: 300, // signed URL will expire in 5 minutes ACL: "public-read", // uploaded file will be publicly readable }; From 8442139d89d2189a653d0ff62cb1d383958acfe6 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 20 Apr 2020 12:58:21 +0200 Subject: [PATCH 09/13] remove unneeded query field from payload --- @app/server/src/plugins/CreateUploadUrlPlugin.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index 66d6ceae..321ca377 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -100,6 +100,7 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ payload verbatim. May be used to track mutations by the client. """ clientMutationId: String + """ You must provide the content type (or MIME type) of the content you intend to upload. For further information about content types, see @@ -118,10 +119,6 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ """ clientMutationId: String - """ - Our root query field type. Allows us to run any query from our mutation payload. - """ - query: Query """ Upload content to this signed URL. """ From 8b73401a3039476eb323b41b2b1a64d86f3644bb Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Mon, 20 Apr 2020 13:05:43 +0200 Subject: [PATCH 10/13] process.env.AWS_BUCKET_UPLOAD --- @app/config/src/index.ts | 2 +- @app/server/src/plugins/CreateUploadUrlPlugin.ts | 9 +++++++++ docs/error_codes.md | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/@app/config/src/index.ts b/@app/config/src/index.ts index d0bc389a..a3dd5690 100644 --- a/@app/config/src/index.ts +++ b/@app/config/src/index.ts @@ -6,7 +6,7 @@ const packageJson = require("../../../package.json"); export const fromEmail = '"PostGraphile Starter" '; export const awsRegion = "us-east-1"; -export const uploadBucket = "my-bucket-name-here"; +export const uploadBucket = process.env.AWS_BUCKET_UPLOAD; export const projectName = packageJson.name.replace(/[-_]/g, " "); export const companyName = projectName; // For copyright ownership export const emailLegalText = diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index 321ca377..9b0959d3 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -145,6 +145,15 @@ const CreateUploadUrlPlugin = makeExtendSchemaPlugin(() => ({ context: OurGraphQLContext, _resolveInfo ) { + if (!uploadBucket) { + const err = new Error( + "Server misconfigured: missing `AWS_BUCKET_UPLOAD` envvar" + ); + // @ts-ignore + err.code = "MSCFG"; + throw err; + } + const user = await getCurrentUser(context.rootPgPool); if (!user) { diff --git a/docs/error_codes.md b/docs/error_codes.md index 83898e4b..8c55c58a 100644 --- a/docs/error_codes.md +++ b/docs/error_codes.md @@ -33,6 +33,7 @@ Rewritten, the above rules state: - DNIED: permission denied - NUNIQ: not unique (from PostgreSQL 23505) - NTFND: not found +- MSCFG: server misconfigured ## Registration From 7d3ff8bf9b7c2409b42d29e1540d3201fd1eacd8 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 9 May 2020 15:51:26 +0200 Subject: [PATCH 11/13] resolve conflict --- yarn.lock | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index fafecb53..68deef5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3174,19 +3174,6 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== -<<<<<<< HEAD -======= -"@types/uuid@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.2.tgz#d680a9c596ef84abf5c4c07a32ffd66d582526f8" - integrity sha512-8Ly3zIPTnT0/8RCU6Kg/G3uTICf9sRwYOpUzSIM3503tLIKcnJPRuinHhXngJUy2MntrEf6dlpOHXJju90Qh5w== - -"@types/valid-url@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/valid-url/-/valid-url-1.0.2.tgz#60fa435ce24bfd5ba107b8d2a80796aeaf3a8f45" - integrity sha1-YPpDXOJL/VuhB7jSqAeWrq86j0U= - ->>>>>>> Extend schema to allow for presigned URLs for uploading files "@types/ws@^6.0.1": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1" From 75475d48e66f650b6eaad00b558184f608ec9714 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 9 May 2020 15:55:43 +0200 Subject: [PATCH 12/13] uuid 8 --- @app/server/package.json | 4 +- .../src/plugins/CreateUploadUrlPlugin.ts | 2 +- yarn.lock | 74 ++++++++----------- 3 files changed, 35 insertions(+), 45 deletions(-) diff --git a/@app/server/package.json b/@app/server/package.json index b5e6dbb8..7eb4dedd 100644 --- a/@app/server/package.json +++ b/@app/server/package.json @@ -23,7 +23,7 @@ "@types/passport-github2": "^1.2.4", "@types/pg": "^7.14.1", "@types/redis": "^2.8.18", - "@types/uuid": "^7.0.2", + "@types/uuid": "^7.0.3", "aws-sdk": "^2.645.0", "body-parser": "^1.19.0", "chalk": "^4.0.0", @@ -46,7 +46,7 @@ "redis": "^3.0.2", "source-map-support": "^0.5.13", "tslib": "^1.11.1", - "uuid": "^7.0.3" + "uuid": "^8.0.0" }, "devDependencies": { "@types/node": "^13.13.4", diff --git a/@app/server/src/plugins/CreateUploadUrlPlugin.ts b/@app/server/src/plugins/CreateUploadUrlPlugin.ts index 9b0959d3..8dac6012 100644 --- a/@app/server/src/plugins/CreateUploadUrlPlugin.ts +++ b/@app/server/src/plugins/CreateUploadUrlPlugin.ts @@ -2,7 +2,7 @@ import { awsRegion, uploadBucket } from "@app/config"; import * as aws from "aws-sdk"; import { gql, makeExtendSchemaPlugin } from "graphile-utils"; import { Pool } from "pg"; -import uuidv4 from "uuid/v4"; +import { v4 as uuidv4 } from "uuid"; import { OurGraphQLContext } from "../middleware/installPostGraphile"; diff --git a/yarn.lock b/yarn.lock index 68deef5c..023df0ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3174,6 +3174,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/uuid@^7.0.3": + version "7.0.3" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-7.0.3.tgz#45cd03e98e758f8581c79c535afbd4fc27ba7ac8" + integrity sha512-PUdqTZVrNYTNcIhLHkiaYzoOIaUi5LFg/XLerAdgvwQrUCx+oSbtoBze1AMyvYbcwzUSNC+Isl58SM4Sm/6COw== + "@types/ws@^6.0.1": version "6.0.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1" @@ -4064,6 +4069,21 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== +aws-sdk@^2.645.0: + version "2.673.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.673.0.tgz#5848900c90cfd8939602019df16fdc38d2a1cdbf" + integrity sha512-OoEPqTLmA5+4uSFf/k4ZLb8cEdx+CwlzovqGf6/gKvb8VrUxe5B5/d2RGlGM777Ke9TmuFhJtTIDugpgc2jo/Q== + dependencies: + buffer "4.9.1" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sdk@^2.668.0: version "2.668.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.668.0.tgz#18b3e64a47f86c109586422596e53dc733117696" @@ -6133,11 +6153,6 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -8206,7 +8221,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -10962,15 +10977,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -needle@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -11171,22 +11177,6 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.44, node-releases@^1.1.53: version "1.1.53" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" @@ -11318,7 +11308,7 @@ npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: semver "^5.6.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.6, npm-packlist@^1.4.4: +npm-packlist@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== @@ -11350,7 +11340,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -13284,7 +13274,7 @@ rc-virtual-list@^1.1.0, rc-virtual-list@^1.1.2: raf "^3.4.1" rc-util "^4.8.0" -rc@^1.2.7, rc@^1.2.8: +rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -13931,7 +13921,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: +rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -14035,7 +14025,7 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: +sax@>=0.6.0, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -14096,7 +14086,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -14887,7 +14877,7 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.10, tar@^4.4.12, tar@^4.4.2, tar@^4.4.8: +tar@^4.4.10, tar@^4.4.12, tar@^4.4.8: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -15653,10 +15643,10 @@ uuid@^7.0.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +uuid@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== v8-compile-cache@^2.0.3: version "2.1.0" From d08e934ab2a5753dd39c027b02a3da18e4b8dc05 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Sat, 9 May 2020 15:57:25 +0200 Subject: [PATCH 13/13] match aws-sdk versions --- @app/server/package.json | 2 +- yarn.lock | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/@app/server/package.json b/@app/server/package.json index 7eb4dedd..90c3c7c1 100644 --- a/@app/server/package.json +++ b/@app/server/package.json @@ -24,7 +24,7 @@ "@types/pg": "^7.14.1", "@types/redis": "^2.8.18", "@types/uuid": "^7.0.3", - "aws-sdk": "^2.645.0", + "aws-sdk": "^2.668.0", "body-parser": "^1.19.0", "chalk": "^4.0.0", "connect-pg-simple": "^6.1.0", diff --git a/yarn.lock b/yarn.lock index 023df0ca..b8144727 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4069,21 +4069,6 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -aws-sdk@^2.645.0: - version "2.673.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.673.0.tgz#5848900c90cfd8939602019df16fdc38d2a1cdbf" - integrity sha512-OoEPqTLmA5+4uSFf/k4ZLb8cEdx+CwlzovqGf6/gKvb8VrUxe5B5/d2RGlGM777Ke9TmuFhJtTIDugpgc2jo/Q== - dependencies: - buffer "4.9.1" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - aws-sdk@^2.668.0: version "2.668.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.668.0.tgz#18b3e64a47f86c109586422596e53dc733117696"