chore(server): apollo server v3 -> v4 (#2880)

* main changes seem to be done?

* lint fix

* minor cleanup

* dataloader clear
This commit is contained in:
Kristaps Fabians Geikins
2024-09-05 12:27:13 +03:00
committed by GitHub
parent 939504fd8d
commit c92938eff3
65 changed files with 1011 additions and 966 deletions
-1
View File
@@ -78,7 +78,6 @@
"core-js-compat/semver": "^7.5.4",
"eslint": "^9.4.0",
"eslint-config-prettier": "^9.1.0",
"graphql": "^15.3.0",
"levelup/bl": ">=1.2.3",
"levelup/semver": ">=5.7.2",
"mocha/serialize-javascript": ">=6.0.2",
@@ -491,7 +491,7 @@ export const resolveGenericStatusCode = (errors: GraphQLErrors) => {
if (
errors.some((e) =>
['UNAUTHENTICATED', 'UNAUTHORIZED_ACCESS_ERROR'].includes(
e.extensions?.code || ''
(e.extensions?.code || '') as string
)
)
)
@@ -499,7 +499,7 @@ export const resolveGenericStatusCode = (errors: GraphQLErrors) => {
if (
errors.some((e) =>
['NOT_FOUND_ERROR', 'STREAM_NOT_FOUND', 'AUTOMATION_NOT_FOUND'].includes(
e.extensions?.code || ''
(e.extensions?.code || '') as string
)
)
)
+37 -47
View File
@@ -21,17 +21,11 @@ import {
import { errorLoggingMiddleware } from '@/logging/errorLogging'
import prometheusClient from 'prom-client'
import {
ApolloServer,
ForbiddenError,
ApolloServerExpressConfig,
ApolloError
} from 'apollo-server-express'
import {
ApolloServerPluginLandingPageLocalDefault,
ApolloServerPluginUsageReportingDisabled,
ApolloServerPluginUsageReporting
} from 'apollo-server-core'
import { ApolloServer } from '@apollo/server'
import { expressMiddleware } from '@apollo/server/express4'
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default'
import { ApolloServerPluginUsageReporting } from '@apollo/server/plugin/usageReporting'
import { ApolloServerPluginUsageReportingDisabled } from '@apollo/server/plugin/disabled'
import { ExecutionParams, SubscriptionServer } from 'subscriptions-transport-ws'
import { execute, subscribe } from 'graphql'
@@ -65,8 +59,13 @@ import { buildMocksConfig } from '@/modules/mocks'
import { defaultErrorHandler } from '@/modules/core/rest/defaultErrorHandler'
import { migrateDbToLatest } from '@/db/migrations'
import { statusCodePlugin } from '@/modules/core/graph/plugins/statusCode'
import { ForbiddenError } from '@/modules/shared/errors'
import { loggingPlugin } from '@/modules/core/graph/plugins/logging'
import { isUserGraphqlError } from '@/modules/shared/helpers/graphqlHelper'
let graphqlServer: ApolloServer
const GRAPHQL_PATH = '/graphql'
let graphqlServer: ApolloServer<GraphQLContext>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type SubscriptionResponse = { errors?: GraphQLError[]; data?: any }
@@ -93,10 +92,7 @@ function logSubscriptionOperation(params: {
const errors = response?.errors || (error ? [error] : [])
if (errors.length) {
for (const error of errors) {
if (
(error instanceof GraphQLError && error.extensions?.code === 'FORBIDDEN') ||
error instanceof ApolloError
) {
if (error instanceof GraphQLError && isUserGraphqlError(error)) {
logger.info(error, errMsg)
} else {
logger.error(error, errMsg)
@@ -112,10 +108,7 @@ function logSubscriptionOperation(params: {
* is that graphql-ws uses an entirely different protocol, so the client-side has to change as well, and so old clients
* will be unable to use any WebSocket/subscriptions functionality with the updated server
*/
function buildApolloSubscriptionServer(
apolloServer: ApolloServer,
server: http.Server
): SubscriptionServer {
function buildApolloSubscriptionServer(server: http.Server): SubscriptionServer {
const schema = ModulesSetup.graphSchema()
// Init metrics
@@ -153,7 +146,6 @@ function buildApolloSubscriptionServer(
schema,
execute,
subscribe,
validationRules: apolloServer.requestOptions.validationRules,
onConnect: async (connectionParams: Record<string, unknown>) => {
metricConnectCounter.inc()
metricConnectedClients.inc()
@@ -235,41 +227,37 @@ function buildApolloSubscriptionServer(
},
{
server,
path: apolloServer.graphqlPath
path: GRAPHQL_PATH
}
)
}
/**
* Create Apollo Server instance
* @param optionOverrides Optionally override ctor options
* @param subscriptionServerResolver If you expect to use subscriptions on this instance,
* pass in a callable that resolves the subscription server
*/
export async function buildApolloServer(
optionOverrides?: Partial<ApolloServerExpressConfig>,
subscriptionServerResolver?: () => SubscriptionServer
): Promise<ApolloServer> {
const debug = optionOverrides?.debug || isDevEnv() || isTestEnv()
export async function buildApolloServer(options?: {
subscriptionServer?: SubscriptionServer
}): Promise<ApolloServer<GraphQLContext>> {
const includeStacktraceInErrorResponses = isDevEnv() || isTestEnv()
const subscriptionServer = options?.subscriptionServer
const schema = ModulesSetup.graphSchema(await buildMocksConfig())
const server = new ApolloServer({
schema,
context: buildContext,
plugins: [
statusCodePlugin,
require('@/logging/apolloPlugin'),
loggingPlugin,
ApolloServerPluginLandingPageLocalDefault({
embed: true,
includeCookies: true
}),
...(subscriptionServerResolver
...(subscriptionServer
? [
{
async serverWillStart() {
return {
async drainServer() {
subscriptionServerResolver().close()
subscriptionServer?.close()
}
}
}
@@ -289,9 +277,8 @@ export async function buildApolloServer(
cache: 'bounded',
persistedQueries: false,
csrfPrevention: true,
formatError: buildErrorFormatter(debug),
debug,
...optionOverrides
formatError: buildErrorFormatter({ includeStacktraceInErrorResponses }),
includeStacktraceInErrorResponses
})
await server.start()
@@ -350,13 +337,20 @@ export async function init() {
// Initialize default modules, including rest api handlers
await ModulesSetup.init(app)
// Init HTTP server & subscription server
const server = http.createServer(app)
const subscriptionServer = buildApolloSubscriptionServer(server)
// Initialize graphql server
// (Apollo Server v3 has an ugly API here - the ApolloServer ctor needs SubscriptionServer,
// and the SubscriptionServer ctor needs ApolloServer...hence the callback passed into buildApolloServer)
// eslint-disable-next-line prefer-const
let subscriptionServer: SubscriptionServer
graphqlServer = await buildApolloServer(undefined, () => subscriptionServer)
graphqlServer.applyMiddleware({ app })
graphqlServer = await buildApolloServer({
subscriptionServer
})
app.use(
GRAPHQL_PATH,
expressMiddleware(graphqlServer, {
context: buildContext
})
)
// Expose prometheus metrics
app.get('/metrics', async (req, res) => {
@@ -368,10 +362,6 @@ export async function init() {
}
})
// Init HTTP server & subscription server
const server = http.createServer(app)
subscriptionServer = buildApolloSubscriptionServer(graphqlServer, server)
// At the very end adding default error handler middleware
app.use(defaultErrorHandler)
@@ -1,3 +1,4 @@
import { buildApolloServer } from '@/app'
import {
deleteRequestById,
getPendingAccessRequest
@@ -28,16 +29,15 @@ import {
useStreamAccessRequest
} from '@/test/graphql/accessRequests'
import { StreamRole } from '@/test/graphql/generated/graphql'
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
import { truncateTables } from '@/test/hooks'
import { EmailSendingServiceMock } from '@/test/mocks/global'
import {
buildNotificationsStateTracker,
NotificationsStateManager
} from '@/test/notificationsHelper'
import { buildAuthenticatedApolloServer } from '@/test/serverHelper'
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
import { ApolloServer } from 'apollo-server-express'
import { expect } from 'chai'
import { noop } from 'lodash'
@@ -55,7 +55,7 @@ const cleanup = async () => {
}
describe('Stream access requests', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
let notificationsStateManager: NotificationsStateManager
const me: BasicTestUser = {
@@ -105,7 +105,10 @@ describe('Stream access requests', () => {
[otherGuysPublicStream, otherGuy],
[myPrivateStream, me]
])
apollo = await buildAuthenticatedApolloServer(me.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
notificationsStateManager = buildNotificationsStateTracker()
})
@@ -1,6 +1,4 @@
'use strict'
const { ForbiddenError } = require('apollo-server-express')
const { ForbiddenError } = require('@/modules/shared/errors')
const {
getApp,
getAllPublicApps,
+1 -1
View File
@@ -10,8 +10,8 @@ const { validateToken, revokeTokenById } = require(`@/modules/core/services/toke
const { revokeRefreshToken } = require(`@/modules/auth/services/apps`)
const { validateScopes } = require(`@/modules/shared`)
const { InvalidAccessCodeRequestError } = require('@/modules/auth/errors')
const { ForbiddenError } = require('apollo-server-errors')
const { Scopes } = require('@speckle/shared')
const { ForbiddenError } = require('@/modules/shared/errors')
// TODO: Secure these endpoints!
module.exports = (app) => {
@@ -13,9 +13,12 @@ import {
StreamBlobsArgs
} from '@/modules/core/graph/generated/graphql'
import { StreamGraphQLReturn } from '@/modules/core/helpers/graphTypes'
import { NotFoundError, ResourceMismatch } from '@/modules/shared/errors'
import {
BadRequestError,
NotFoundError,
ResourceMismatch
} from '@/modules/shared/errors'
import { Nullable } from '@speckle/shared'
import { UserInputError } from 'apollo-server-errors'
const streamBlobResolvers = {
async blobs(parent: StreamGraphQLReturn, args: StreamBlobsArgs | ProjectBlobsArgs) {
@@ -47,7 +50,7 @@ const streamBlobResolvers = {
})) as Nullable<BlobStorageRecord>
} catch (err: unknown) {
if (err instanceof NotFoundError) return null
if (err instanceof ResourceMismatch) throw new UserInputError(err.message)
if (err instanceof ResourceMismatch) throw new BadRequestError(err.message)
throw err
}
}
@@ -1,18 +1,17 @@
const { buildApolloServer } = require('@/app')
const { addLoadersToCtx } = require('@/modules/shared/middleware')
const { truncateTables } = require('@/test/hooks')
const { Roles, AllScopes } = require('@/modules/core/helpers/mainConstants')
const { createStream } = require('@/modules/core/services/streams')
const { createUser } = require('@/modules/core/services/users')
const crs = require('crypto-random-string')
const { gql } = require('apollo-server-express')
const { gql } = require('graphql-tag')
const { createBlobs } = require('@/modules/blobstorage/tests/helpers')
const { expect } = require('chai')
const { Users, Streams } = require('@/modules/core/dbSchema')
const { createAuthedTestContext, executeOperation } = require('@/test/graphqlHelper')
describe('Blobs graphql @blobstorage', () => {
/** @type {import('apollo-server-express').ApolloServer} */
let apollo
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
let graphqlServer
const user = {
name: 'Baron Von Blubba',
email: 'zebarron@bubble.bobble',
@@ -21,16 +20,10 @@ describe('Blobs graphql @blobstorage', () => {
before(async () => {
await truncateTables(['blob_storage', Users.name, Streams.name])
user.id = await createUser(user)
apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: crs({ length: 10 }),
role: Roles.Server.User,
token: 'asd',
scopes: AllScopes
})
})
graphqlServer = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(user.id)
}
})
it('Stream has blob metadata for a single blob', async () => {
@@ -50,13 +43,12 @@ describe('Blobs graphql @blobstorage', () => {
`
const streamId = await createStream({ ownerId: user.id })
const [blob] = await createBlobs({ streamId, number: 1 })
const result = await apollo.executeOperation({
query,
variables: {
streamId,
blobId: blob.id
}
const result = await executeOperation(graphqlServer, query, {
streamId,
blobId: blob.id
})
const blobMetadata = result.data.stream.blob
expect(blobMetadata.id).to.equal(blob.id)
expect(blobMetadata.fileSize).to.equal(blob.fileSize)
@@ -79,7 +71,7 @@ describe('Blobs graphql @blobstorage', () => {
const number = 10
const fileSize = 123
await createBlobs({ streamId, number, fileSize })
const result = await apollo.executeOperation({ query, variables: { streamId } })
const result = await executeOperation(graphqlServer, query, { streamId })
expect(result.data.stream.blobs.totalCount).to.equal(number)
expect(result.data.stream.blobs.totalSize).to.equal(number * fileSize)
})
@@ -1,5 +1,4 @@
import { pubsub } from '@/modules/shared/utils/subscriptions'
import { ForbiddenError as ApolloForbiddenError } from 'apollo-server-express'
import { ForbiddenError } from '@/modules/shared/errors'
import { getStream } from '@/modules/core/services/streams'
import { Roles } from '@/modules/core/helpers/mainConstants'
@@ -77,7 +76,7 @@ const getStreamComment = async (
const comment = await getComment({ id: commentId, userId: ctx.userId })
if (comment?.streamId !== streamId)
throw new ApolloForbiddenError('You do not have access to this comment.')
throw new ForbiddenError('You do not have access to this comment.')
return comment
}
@@ -285,21 +284,21 @@ export = {
Stream: {
async commentCount(parent, _args, context) {
if (context.role === Roles.Server.ArchivedUser)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
return await context.loaders.streams.getCommentThreadCount.load(parent.id)
}
},
Commit: {
async commentCount(parent, args, context) {
if (context.role === Roles.Server.ArchivedUser)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
return await getResourceCommentCount({ resourceId: parent.id })
}
},
Object: {
async commentCount(parent, args, context) {
if (context.role === Roles.Server.ArchivedUser)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
return await getResourceCommentCount({ resourceId: parent.id })
}
},
@@ -368,17 +367,7 @@ export = {
projectId: args.streamId,
authCtx: context
})
// const stream = await getStream({
// streamId: args.streamId,
// userId: context.userId
// })
// if (!stream) {
// throw new ApolloError('Stream not found')
// }
// if (!stream.isPublic && !context.auth) {
// return false
// }
await pubsub.publish(CommentSubscriptions.ViewerActivity, {
userViewerActivity: args.data,
streamId: args.streamId,
@@ -396,7 +385,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
await pubsub.publish(CommentSubscriptions.CommentThreadActivity, {
commentThreadActivity: { type: 'reply-typing-status', data: args.data },
@@ -408,7 +397,7 @@ export = {
async commentCreate(parent, args, context) {
if (!context.userId)
throw new ApolloForbiddenError('Only registered users can comment.')
throw new ForbiddenError('Only registered users can comment.')
const stream = await getStream({
streamId: args.input.streamId,
@@ -416,7 +405,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
const comment = await createComment({
userId: context.userId,
@@ -441,13 +430,8 @@ export = {
requireProjectRole: true
})
const matchUser = !stream.role
try {
await editComment({ userId: context.userId!, input: args.input, matchUser })
return true
} catch (err) {
if (err instanceof ForbiddenError) throw new ApolloForbiddenError(err.message)
throw err
}
await editComment({ userId: context.userId!, input: args.input, matchUser })
return true
},
// used for flagging a comment as viewed
@@ -467,13 +451,7 @@ export = {
requireProjectRole: true
})
let updatedComment
try {
updatedComment = await archiveComment({ ...args, userId: context.userId! }) // NOTE: permissions check inside service
} catch (err) {
if (err instanceof ForbiddenError) throw new ApolloForbiddenError(err.message)
throw err
}
const updatedComment = await archiveComment({ ...args, userId: context.userId! }) // NOTE: permissions check inside service
await addCommentArchivedActivity({
streamId: args.streamId,
@@ -488,7 +466,7 @@ export = {
async commentReply(parent, args, context) {
if (!context.userId)
throw new ApolloForbiddenError('Only registered users can comment.')
throw new ForbiddenError('Only registered users can comment.')
const stream = await getStream({
streamId: args.input.streamId,
@@ -496,7 +474,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
const reply = await createCommentReply({
authorId: context.userId,
@@ -528,7 +506,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
// dont report users activity to himself
if (context.userId && context.userId === payload.authorId) {
@@ -552,7 +530,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
// if we're listening for a stream's root comments events
if (!variables.resourceIds) {
@@ -601,7 +579,7 @@ export = {
})
if (!stream?.allowPublicComments && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
return (
payload.streamId === variables.streamId &&
@@ -630,7 +608,7 @@ export = {
])
if (!stream?.isPublic && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
// dont report users activity to himself
if (
@@ -665,7 +643,7 @@ export = {
])
if (!(stream?.isDiscoverable || stream?.isPublic) && !stream?.role)
throw new ApolloForbiddenError('You are not authorized.')
throw new ForbiddenError('You are not authorized.')
if (!target.resourceIdString) {
return true
@@ -2,15 +2,14 @@ const expect = require('chai').expect
const crs = require('crypto-random-string')
const { buildApolloServer } = require('@/app')
const { addLoadersToCtx } = require('@/modules/shared/middleware')
const { beforeEachContext } = require('@/test/hooks')
const { Roles, AllScopes } = require('@/modules/core/helpers/mainConstants')
const { Roles } = require('@/modules/core/helpers/mainConstants')
const {
grantPermissionsStream,
updateStream
} = require('@/modules/core/services/streams')
const { createUser } = require('@/modules/core/services/users')
const { gql } = require('apollo-server-express')
const { gql } = require('graphql-tag')
const { createStream } = require('@/modules/core/services/streams')
const { createObject } = require('@/modules/core/services/objects')
const { createComment } = require('@/modules/comments/services')
@@ -18,6 +17,11 @@ const { createCommitByBranchName } = require('@/modules/core/services/commits')
const {
convertBasicStringToDocument
} = require('@/modules/core/services/richTextEditorService')
const {
createTestContext,
createAuthedTestContext,
executeOperation
} = require('@/test/graphqlHelper')
function buildCommentInputFromString(textString) {
return convertBasicStringToDocument(textString)
@@ -27,7 +31,7 @@ const testForbiddenResponse = (result) => {
expect(result.errors, 'This should have failed').to.exist
expect(result.errors.length).to.be.above(0)
expect(result.errors[0].extensions.code).to.match(
/(STREAM_INVALID_ACCESS_ERROR|FORBIDDEN)/
/(STREAM_INVALID_ACCESS_ERROR|FORBIDDEN|UNAUTHORIZED_ACCESS_ERROR)/
)
}
@@ -40,14 +44,32 @@ const testResult = (shouldSucceed, result, successTests) => {
}
}
/**
* @typedef {{
* apollo: import('@/test/graphqlHelper').ServerAndContext,
* resources: {
* streamId: string,
* objectId: string,
* commentId: string,
* testActorId: string
* },
* shouldSucceed: boolean,
* streamId: string
* }} TestContext
*/
/**
* @param {TestContext} param0
*/
const writeComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($input: CommentCreateInput!) {
commentCreate(input: $input)
}
`,
variables: {
{
input: {
streamId: resources.streamId,
text: buildCommentInputFromString('foo'),
@@ -56,16 +78,20 @@ const writeComment = async ({ apollo, resources, shouldSucceed }) => {
resources: [{ resourceId: resources.streamId, resourceType: 'stream' }]
}
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentCreate).to.be.string
expect(res.data.commentCreate.length).to.equal(10)
})
}
/**
* @param {TestContext} param0
*/
const broadcastViewerActivity = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($streamId: String!, $resourceId: String!, $data: JSONObject) {
userViewerActivityBroadcast(
streamId: $streamId
@@ -74,20 +100,24 @@ const broadcastViewerActivity = async ({ apollo, resources, shouldSucceed }) =>
)
}
`,
variables: {
{
streamId: resources.streamId,
data: {},
resourceId: resources.objectId
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.userViewerActivityBroadcast).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const broadcastCommentActivity = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($streamId: String!, $commentId: String!, $data: JSONObject) {
userCommentThreadActivityBroadcast(
streamId: $streamId
@@ -96,36 +126,43 @@ const broadcastCommentActivity = async ({ apollo, resources, shouldSucceed }) =>
)
}
`,
variables: {
{
streamId: resources.streamId,
data: {},
commentId: resources.commentId
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.userCommentThreadActivityBroadcast).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const viewAComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($streamId: String!, $commentId: String!) {
commentView(streamId: $streamId, commentId: $commentId)
}
`,
variables: {
{
streamId: resources.streamId,
commentId: resources.commentId
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentView).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const archiveMyComment = async ({ apollo, resources, shouldSucceed }) => {
const context = await apollo.context()
const context = apollo.context
const { id: commentId } = await createComment({
userId: context.userId,
input: {
@@ -139,40 +176,47 @@ const archiveMyComment = async ({ apollo, resources, shouldSucceed }) => {
]
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($streamId: String!, $commentId: String!) {
commentArchive(streamId: $streamId, commentId: $commentId)
}
`,
variables: { streamId: resources.streamId, commentId }
})
{ streamId: resources.streamId, commentId }
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentArchive).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const archiveOthersComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($streamId: String!, $commentId: String!) {
commentArchive(streamId: $streamId, commentId: $commentId)
}
`,
variables: {
{
streamId: resources.streamId,
commentId: resources.commentId
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentArchive).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const editMyComment = async ({ apollo, resources, shouldSucceed }) => {
const context = await apollo.context()
const { id: commentId } = await createComment({
userId: context.userId,
userId: apollo.context.userId,
input: {
streamId: resources.streamId,
text: buildCommentInputFromString('i wrote this myself'),
@@ -184,13 +228,14 @@ const editMyComment = async ({ apollo, resources, shouldSucceed }) => {
]
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($input: CommentEditInput!) {
commentEdit(input: $input)
}
`,
variables: {
{
input: {
streamId: resources.streamId,
id: commentId,
@@ -198,20 +243,24 @@ const editMyComment = async ({ apollo, resources, shouldSucceed }) => {
blobIds: []
}
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentEdit).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const editOthersComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($input: CommentEditInput!) {
commentEdit(input: $input)
}
`,
variables: {
{
input: {
streamId: resources.streamId,
id: resources.commentId,
@@ -221,20 +270,24 @@ const editOthersComment = async ({ apollo, resources, shouldSucceed }) => {
blobIds: []
}
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentEdit).to.be.true
})
}
/**
* @param {TestContext} param0
*/
const replyToAComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
mutation ($input: ReplyCreateInput!) {
commentReply(input: $input)
}
`,
variables: {
{
input: {
streamId: resources.streamId,
parentComment: resources.commentId,
@@ -245,16 +298,20 @@ const replyToAComment = async ({ apollo, resources, shouldSucceed }) => {
data: {}
}
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.commentReply).to.be.string
expect(res.data.commentReply.length).to.equal(10)
})
}
/**
* @param {TestContext} param0
*/
const queryComment = async ({ apollo, resources, shouldSucceed }) => {
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($id: String!, $streamId: String!) {
comment(id: $id, streamId: $streamId) {
id
@@ -270,16 +327,20 @@ const queryComment = async ({ apollo, resources, shouldSucceed }) => {
}
}
`,
variables: {
{
id: resources.commentId,
streamId: resources.streamId
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.comment.id).to.exist
expect(res.data.comment.id).to.equal(resources.commentId)
})
}
/**
* @param {TestContext} param0
*/
const queryComments = async ({ apollo, resources, shouldSucceed }) => {
const object = {
foo: 123,
@@ -304,8 +365,9 @@ const queryComments = async ({ apollo, resources, shouldSucceed }) => {
)
)
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($streamId: String!, $resources: [ResourceIdentifierInput]) {
comments(streamId: $streamId, resources: $resources) {
totalCount
@@ -318,20 +380,23 @@ const queryComments = async ({ apollo, resources, shouldSucceed }) => {
}
}
`,
variables: {
{
streamId: resources.streamId,
resources: [
// i expected this to work as intersection, but it works as union
{ resourceId: objectId, resourceType: 'object' }
]
}
})
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.comments.totalCount).to.be.equal(numberOfComments)
expect(res.data.comments.items.map((i) => i.id)).to.be.equalInAnyOrder(commentIds)
})
}
/**
* @param {TestContext} param0
*/
const queryStreamCommentCount = async ({ apollo, resources, shouldSucceed }) => {
await createComment({
userId: resources.testActorId,
@@ -344,8 +409,9 @@ const queryStreamCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($id: String!) {
stream(id: $id) {
id
@@ -353,13 +419,16 @@ const queryStreamCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
}
`,
variables: { id: resources.streamId }
})
{ id: resources.streamId }
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.stream.commentCount).to.be.greaterThanOrEqual(1)
})
}
/**
* @param {TestContext} param0
*/
const queryObjectCommentCount = async ({ apollo, resources, shouldSucceed }) => {
const objectId = await createObject({
streamId: resources.streamId,
@@ -379,8 +448,9 @@ const queryObjectCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($id: String!, $objectId: String!) {
stream(id: $id) {
object(id: $objectId) {
@@ -389,13 +459,16 @@ const queryObjectCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
}
`,
variables: { id: resources.streamId, objectId }
})
{ id: resources.streamId, objectId }
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.stream.object.commentCount).to.equal(1)
})
}
/**
* @param {TestContext} param0
*/
const queryCommitCommentCount = async ({ apollo, resources, shouldSucceed }) => {
const objectId = await createObject({
streamId: resources.streamId,
@@ -422,8 +495,9 @@ const queryCommitCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($id: String!, $commitId: String!) {
stream(id: $id) {
commit(id: $commitId) {
@@ -432,13 +506,16 @@ const queryCommitCommentCount = async ({ apollo, resources, shouldSucceed }) =>
}
}
`,
variables: { id: resources.streamId, commitId }
})
{ id: resources.streamId, commitId }
)
testResult(shouldSucceed, res, (res) => {
expect(res.data.stream.commit.commentCount).to.equal(1)
})
}
/**
* @param {TestContext} param0
*/
const queryCommitCollectionCommentCount = async ({
apollo,
resources,
@@ -469,8 +546,9 @@ const queryCommitCollectionCommentCount = async ({
}
})
const res = await apollo.executeOperation({
query: gql`
const res = await executeOperation(
apollo,
gql`
query ($id: String!) {
otherUser(id: $id) {
commits {
@@ -481,8 +559,8 @@ const queryCommitCollectionCommentCount = async ({
}
}
`,
variables: { id: resources.testActorId }
})
{ id: resources.testActorId }
)
testResult(shouldSucceed, res, (res) => {
res.data.otherUser.commits.items
.map((i) => i.commentCount)
@@ -830,19 +908,20 @@ describe('Graphql @comments', () => {
userContext.streamData.forEach((streamContext) => {
const stream = streamContext.stream
let resources
/**
* @type {import('@/test/graphqlHelper').ServerAndContext}
*/
let apollo
before(async () => {
apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: user?.id,
role: user?.role,
token: 'asd',
scopes: AllScopes
})
})
apollo = {
apollo: await buildApolloServer(),
context: user
? createAuthedTestContext(user.id, {
...(user.role ? { role: user.role } : {})
})
: createTestContext()
}
if (user && stream.role) {
await grantPermissionsStream({
@@ -35,8 +35,7 @@ const {
} = require('@/modules/comments/services/commentTextService')
const { range } = require('lodash')
const { buildApolloServer } = require('@/app')
const { addLoadersToCtx } = require('@/modules/shared/middleware')
const { Roles, AllScopes } = require('@/modules/core/helpers/mainConstants')
const { AllScopes } = require('@/modules/core/helpers/mainConstants')
const { createAuthTokenForUser } = require('@/test/authHelper')
const { uploadBlob } = require('@/test/blobHelper')
const { Comments } = require('@/modules/core/dbSchema')
@@ -47,6 +46,7 @@ const {
} = require('@/test/notificationsHelper')
const { NotificationType } = require('@/modules/notifications/helpers/types')
const { EmailSendingServiceMock } = require('@/test/mocks/global')
const { createAuthedTestContext } = require('@/test/graphqlHelper')
function buildCommentInputFromString(textString) {
return convertBasicStringToDocument(textString)
@@ -999,7 +999,7 @@ describe('Comments @comments', () => {
})
describe('when authenticated', () => {
/** @type {import('apollo-server-express').ApolloServer} */
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
let apollo
let userToken
let blob1
@@ -1008,16 +1008,10 @@ describe('Comments @comments', () => {
const scopes = AllScopes
// Init apollo instance w/ authenticated context
apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: user.id,
role: Roles.Server.User,
token: 'asd',
scopes
})
})
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(user.id)
}
// Init token for authenticating w/ REST API
userToken = await createAuthTokenForUser(user.id, scopes)
@@ -1,10 +1,29 @@
/* eslint-disable camelcase */
/* istanbul ignore file */
const { ApolloError } = require('apollo-server-express')
const prometheusClient = require('prom-client')
const { graphqlLogger } = require('@/logging/logging')
const { redactSensitiveVariables } = require('@/logging/loggingHelper')
const { GraphQLError } = require('graphql')
import prometheusClient from 'prom-client'
import { graphqlLogger } from '@/logging/logging'
import { redactSensitiveVariables } from '@/logging/loggingHelper'
import { FieldNode, GraphQLError, SelectionNode } from 'graphql'
import { ApolloServerPlugin } from '@apollo/server'
import { GraphQLContext } from '@/modules/shared/helpers/typeHelper'
import { isUserGraphqlError } from '@/modules/shared/helpers/graphqlHelper'
type ApolloLoggingPluginTransaction = {
start: number
op: string
name: string
finish: () => void
}
declare module '@apollo/server' {
interface GraphQLRequest {
/**
* Set and used in our apollo logging plugin
*/
transaction?: ApolloLoggingPluginTransaction
}
}
const isFieldNode = (node: SelectionNode): node is FieldNode => node.kind === 'Field'
const metricCallCount = new prometheusClient.Counter({
name: 'speckle_server_apollo_calls',
@@ -12,19 +31,33 @@ const metricCallCount = new prometheusClient.Counter({
labelNames: ['actionName']
})
/** @type {import('apollo-server-core').PluginDefinition} */
module.exports = {
// eslint-disable-next-line no-unused-vars
requestDidStart(ctx) {
export const loggingPlugin: ApolloServerPlugin<GraphQLContext> = {
requestDidStart: async () => {
const apolloRequestStart = Date.now()
return {
didResolveOperation(ctx) {
let logger = ctx.context.log || graphqlLogger
const auth = ctx.context
didResolveOperation: async (ctx) => {
let logger = ctx.contextValue.log || graphqlLogger
if (!ctx.operation) {
logger.debug('Attempted to log a GQL operation without an operation')
return
}
const firstSelectedField =
ctx.operation.selectionSet.selections.find(isFieldNode)
if (!firstSelectedField) {
logger.debug(
'Attempted to log a GQL operation without a top-level field selection'
)
return
}
const auth = ctx.contextValue
const userId = auth?.userId
const op = `GQL ${ctx.operation.operation} ${ctx.operation.selectionSet.selections[0].name.value}`
const name = `GQL ${ctx.operation.selectionSet.selections[0].name.value}`
const actionName = `${ctx.operation.operation} ${firstSelectedField.name.value}`
const op = `GQL ${actionName}`
const name = `GQL ${firstSelectedField.name.value}`
const kind = ctx.operation.operation
const query = ctx.request.query
const variables = ctx.request.variables
@@ -43,23 +76,23 @@ module.exports = {
op,
name,
finish: () => {
//TODO add tracing with opentelemetry
// TODO: add tracing with opentelemetry
}
}
try {
const actionName = `${ctx.operation.operation} ${ctx.operation.selectionSet.selections[0].name.value}`
logger = logger.child({ actionName })
metricCallCount.labels(actionName).inc()
} catch (e) {
logger.error({ err: e, transaction }, 'Error while defining action name')
}
ctx.request.http
ctx.request.transaction = transaction
ctx.context.log = logger
ctx.contextValue.log = logger
},
didEncounterErrors(ctx) {
let logger = ctx.context.log || graphqlLogger
didEncounterErrors: async (ctx) => {
let logger = ctx.contextValue.log || graphqlLogger
logger = logger.child({
apollo_query_duration_ms: Date.now() - apolloRequestStart
})
@@ -77,10 +110,7 @@ module.exports = {
graphql_variables: variables
})
}
if (
(err instanceof GraphQLError && err.extensions?.code === 'FORBIDDEN') ||
err instanceof ApolloError
) {
if (err instanceof GraphQLError && isUserGraphqlError(err)) {
logger.info(
{ err },
'{graphql_operation_value} failed after {apollo_query_duration_ms} ms'
@@ -93,12 +123,13 @@ module.exports = {
}
}
},
willSendResponse(ctx) {
const logger = ctx.context.log || graphqlLogger
willSendResponse: async (ctx) => {
const logger = ctx.contextValue.log || graphqlLogger
if (ctx.request.transaction) {
ctx.request.transaction.finish()
}
logger.info(
{
apollo_query_duration_ms: Date.now() - apolloRequestStart
@@ -1,8 +1,9 @@
import { RateLimitError } from '@/modules/core/errors/ratelimit'
import { BaseError } from '@/modules/shared/errors'
import { Nullable } from '@speckle/shared'
import type { ApolloServerPlugin } from 'apollo-server-plugin-base'
import type { ApolloServerPlugin } from '@apollo/server'
import type { GraphQLError } from 'graphql'
import { GraphQLContext } from '@/modules/shared/helpers/typeHelper'
const getErrorCode = (e: GraphQLError): Nullable<string> => {
const extensionsCode = e.extensions?.code as string
@@ -15,7 +16,7 @@ const getErrorCode = (e: GraphQLError): Nullable<string> => {
return infoCode
}
export const statusCodePlugin: ApolloServerPlugin = {
export const statusCodePlugin: ApolloServerPlugin<GraphQLContext> = {
requestDidStart: async () => {
return {
willSendResponse: async (reqCtx) => {
@@ -1,6 +1,4 @@
'use strict'
const { ForbiddenError } = require('apollo-server-express')
const { ForbiddenError } = require('@/modules/shared/errors')
const {
createPersonalAccessToken,
revokeToken,
@@ -1,7 +1,4 @@
'use strict'
const { CommitNotFoundError } = require('@/modules/core/errors/commit')
const { UserInputError } = require('apollo-server-express')
const { withFilter } = require('graphql-subscriptions')
const {
pubsub,
@@ -41,6 +38,7 @@ const {
const { StreamInvalidAccessError } = require('@/modules/core/errors/stream')
const { Roles } = require('@speckle/shared')
const { toProjectIdWhitelist } = require('@/modules/core/helpers/token')
const { BadRequestError } = require('@/modules/shared/errors')
// subscription events
const COMMIT_CREATED = CommitPubsubEvents.CommitCreated
@@ -61,7 +59,7 @@ const getUserCommits = async (publicOnly, userId, args, streamIdWhitelist) => {
streamIdWhitelist
})
if (args.limit && args.limit > 100)
throw new UserInputError(
throw new BadRequestError(
'Cannot return more than 100 items, please use pagination.'
)
const { commits: items, cursor } = await getCommitsByUserId({
@@ -1,4 +1,3 @@
import { UserInputError } from 'apollo-server-express'
import {
getStream,
getStreams,
@@ -49,6 +48,7 @@ import { queryAllResourceInvitesFactory } from '@/modules/serverinvites/reposito
import db from '@/db/knex'
import { getInvitationTargetUsersFactory } from '@/modules/serverinvites/services/retrieval'
import { getUsers } from '@/modules/core/repositories/users'
import { BadRequestError } from '@/modules/shared/errors'
const getUserStreamsCore = async (
forOtherUser: boolean,
@@ -125,7 +125,7 @@ export = {
async adminStreams(parent, args, ctx) {
if (args.limit && args.limit > 50)
throw new UserInputError('Cannot return more than 50 items at a time.')
throw new BadRequestError('Cannot return more than 50 items at a time.')
const { streams, totalCount } = await getStreams({
offset: args.offset,
@@ -200,7 +200,7 @@ export = {
const { limit, cursor } = args
if (userId !== requestedUserId)
throw new UserInputError("Cannot view another user's favorite streams")
throw new BadRequestError("Cannot view another user's favorite streams")
return await getFavoriteStreamsCollection({
userId,
@@ -305,12 +305,14 @@ export = {
const { streamId, favorited } = args
const { userId, resourceAccessRules } = ctx
return await favoriteStream({
const stream = await favoriteStream({
userId: userId!,
streamId,
favorited,
userResourceAccessRules: resourceAccessRules
})
return stream
},
async streamLeave(_parent, args, ctx) {
@@ -1,5 +1,3 @@
'use strict'
const { UserInputError } = require('apollo-server-express')
const {
getUser,
getUserByEmail,
@@ -28,6 +26,7 @@ const {
findServerInvitesFactory
} = require('@/modules/serverinvites/repositories/serverInvites')
const db = require('@/db/knex')
const { BadRequestError } = require('@/modules/shared/errors')
/** @type {import('@/modules/core/graph/generated/graphql').Resolvers} */
module.exports = {
@@ -60,7 +59,7 @@ module.exports = {
else await validateScopes(context.scopes, Scopes.Users.Read)
if (!args.id && !context.userId) {
throw new UserInputError('You must provide an user id.')
throw new BadRequestError('You must provide an user id.')
}
return await getUser(args.id || context.userId)
@@ -81,10 +80,10 @@ module.exports = {
await validateScopes(context.scopes, Scopes.Users.Read)
if (args.query.length < 3)
throw new UserInputError('Search query must be at least 3 carachters.')
throw new BadRequestError('Search query must be at least 3 carachters.')
if (args.limit && args.limit > 100)
throw new UserInputError(
throw new BadRequestError(
'Cannot return more than 100 items, please use pagination.'
)
@@ -173,7 +172,7 @@ module.exports = {
const user = await getUser(context.userId)
if (args.userConfirmation.email !== user.email) {
throw new UserInputError('Malformed input: emails do not match.')
throw new BadRequestError('Malformed input: emails do not match.')
}
// The below are not really needed anymore as we've added the hasRole and hasScope
@@ -1,7 +1,9 @@
const _ = require('lodash')
const VError = require('verror')
const { ZodError } = require('zod')
const { fromZodError } = require('zod-validation-error')
import { ApolloServerOptions, BaseContext } from '@apollo/server'
import { GraphQLError } from 'graphql'
import _ from 'lodash'
import { VError } from 'verror'
import { ZodError } from 'zod'
import { fromZodError } from 'zod-validation-error'
/**
* Some VError implementation details that we want to remove from object representations
@@ -11,32 +13,36 @@ const VERROR_TRASH_PROPS = ['jse_shortmsg', 'jse_cause', 'jse_info']
/**
* Builds apollo server error formatter
* @param {boolean} debug
* @returns {(e: import('graphql').GraphQLError) => import('graphql').GraphQLFormattedError}
*/
function buildErrorFormatter(debug) {
export function buildErrorFormatter(params: {
includeStacktraceInErrorResponses: boolean
}): ApolloServerOptions<BaseContext>['formatError'] {
const { includeStacktraceInErrorResponses } = params
// TODO: Add support for client-aware errors and obfuscate everything else
return function (error) {
const debugMode = debug
const realError = error.originalError ? error.originalError : error
return function (formattedError, error) {
let realError = error || formattedError
if (realError instanceof GraphQLError && realError.originalError) {
realError = realError.originalError
}
// If error is a ZodError, convert its message to something more readable
if (realError instanceof ZodError) {
return {
...error,
...formattedError,
message: fromZodError(realError).message,
extensions: { ...error.extensions, code: 'BAD_REQUEST' }
extensions: { ...formattedError.extensions, code: 'BAD_REQUEST' }
}
}
// If error isn't a VError child, don't do anything extra
if (!(realError instanceof VError)) {
return error
return formattedError
}
// Converting VError based error to Apollo's format
const extensions = {
...(error.extensions || {}),
...(formattedError.extensions || {}),
...(VError.info(realError) || {})
}
@@ -47,7 +53,7 @@ function buildErrorFormatter(debug) {
if (extensions.exception) {
extensions.exception = _.omit(extensions.exception, VERROR_TRASH_PROPS)
if (debugMode) {
if (includeStacktraceInErrorResponses) {
extensions.exception.stacktrace = VError.fullStack(realError)
} else {
delete extensions.exception.stacktrace
@@ -55,14 +61,10 @@ function buildErrorFormatter(debug) {
}
return {
message: error.message,
locations: error.locations,
path: error.path,
message: formattedError.message,
locations: formattedError.locations,
path: formattedError.path,
extensions
}
}
}
module.exports = {
buildErrorFormatter
}
@@ -4,7 +4,6 @@ import {
ProjectModelsTreeArgs,
StreamBranchesArgs
} from '@/modules/core/graph/generated/graphql'
import { UserInputError } from 'apollo-server-core'
import { getBranchesByStreamId } from '@/modules/core/services/branches'
import {
getStructuredProjectModels,
@@ -19,6 +18,7 @@ import { last } from 'lodash'
import { Merge } from 'type-fest'
import { ModelsTreeItemGraphQLReturn } from '@/modules/core/helpers/graphTypes'
import { getMaximumProjectModelsPerPage } from '@/modules/shared/helpers/envHelper'
import { BadRequestError } from '@/modules/shared/errors'
export async function getStructuredStreamModels(streamId: string) {
return getStructuredProjectModels(streamId)
@@ -30,7 +30,7 @@ export async function getPaginatedStreamBranches(
) {
const maxProjectModelsPerPage = getMaximumProjectModelsPerPage()
if (params.limit && params.limit > maxProjectModelsPerPage)
throw new UserInputError(
throw new BadRequestError(
`Cannot return more than ${maxProjectModelsPerPage} items, please use pagination.`
)
const { items, cursor, totalCount } = await getBranchesByStreamId({
@@ -13,14 +13,14 @@ import {
getCommitsByStreamId,
getCommitsTotalCountByStreamId
} from '@/modules/core/services/commits'
import { UserInputError } from 'apollo-server-core'
import { BadRequestError } from '@/modules/shared/errors'
export async function getPaginatedStreamCommits(
streamId: string,
params: StreamCommitsArgs
) {
if (params.limit && params.limit > 100)
throw new UserInputError(
throw new BadRequestError(
'Cannot return more than 100 items, please use pagination.'
)
const { commits: items, cursor } = await getCommitsByStreamId({
@@ -41,7 +41,7 @@ export async function getPaginatedBranchCommits(
params: PaginatedBranchCommitsParams & { filter?: Nullable<ModelVersionsFilter> }
) {
if (params.limit && params.limit > 100)
throw new UserInputError(
throw new BadRequestError(
'Cannot return more than 100 items, please use pagination.'
)
@@ -1,8 +1,11 @@
const { authorizeResolver } = require(`@/modules/shared`)
const { Roles } = require('@/modules/core/helpers/mainConstants')
const { LogicError } = require('@/modules/shared/errors')
const { ForbiddenError, UserInputError } = require('apollo-server-express')
const {
LogicError,
ForbiddenError,
BadRequestError
} = require('@/modules/shared/errors')
const {
StreamInvalidAccessError,
StreamAccessUpdateError
@@ -170,7 +173,7 @@ async function addOrUpdateStreamCollaborator(
if (role === Roles.Stream.Owner) {
const userServerRole = await ServerAcl.knex().where({ userId }).first()
if (userServerRole.role === Roles.Server.Guest)
throw new UserInputError('Server guests cannot own streams')
throw new BadRequestError('Server guests cannot own streams')
}
const stream = await grantStreamPermissions({
@@ -1,3 +1,4 @@
import { buildApolloServer } from '@/app'
import { Commits, Streams, Users } from '@/modules/core/dbSchema'
import { Roles } from '@/modules/core/helpers/mainConstants'
import { getCommits } from '@/modules/core/repositories/commits'
@@ -5,14 +6,14 @@ import { createBranch } from '@/modules/core/services/branches'
import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService'
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
import { deleteCommits, moveCommits } from '@/test/graphql/commits'
import { truncateTables } from '@/test/hooks'
import {
buildAuthenticatedApolloServer,
buildUnauthenticatedApolloServer
} from '@/test/serverHelper'
createAuthedTestContext,
createTestContext,
ServerAndContext
} from '@/test/graphqlHelper'
import { truncateTables } from '@/test/hooks'
import { BasicTestCommit, createTestCommits } from '@/test/speckle-helpers/commitHelper'
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
import { ApolloServer } from 'apollo-server-express'
import { expect } from 'chai'
import { times } from 'lodash'
import { describe } from 'mocha'
@@ -120,7 +121,7 @@ describe('Batch commits', () => {
]
const buildBatchActionInvoker =
(apollo: ApolloServer) => (type: BatchActionType, commitIds: string[]) => {
(apollo: ServerAndContext) => (type: BatchActionType, commitIds: string[]) => {
if (type === BatchActionType.Delete) {
return deleteCommits(apollo, { input: { commitIds } })
} else if (type === BatchActionType.Move) {
@@ -135,11 +136,14 @@ describe('Batch commits', () => {
type BatchActionInvoker = ReturnType<typeof buildBatchActionInvoker>
describe('when authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
let invokeBatchAction: BatchActionInvoker
before(async () => {
apollo = await buildAuthenticatedApolloServer(me.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
invokeBatchAction = buildBatchActionInvoker(apollo)
})
@@ -268,11 +272,14 @@ describe('Batch commits', () => {
})
describe('when not authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
let invokeBatchAction: BatchActionInvoker
before(async () => {
apollo = await buildUnauthenticatedApolloServer()
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
invokeBatchAction = buildBatchActionInvoker(apollo)
})
@@ -1,14 +1,14 @@
import { buildApolloServer } from '@/app'
import { Commits, Streams, Users } from '@/modules/core/dbSchema'
import { Roles } from '@/modules/core/helpers/mainConstants'
import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService'
import { Nullable } from '@/modules/shared/helpers/typeHelper'
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
import { readOtherUsersCommits, readOwnCommits } from '@/test/graphql/commits'
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
import { truncateTables } from '@/test/hooks'
import { buildAuthenticatedApolloServer } from '@/test/serverHelper'
import { createTestCommit } from '@/test/speckle-helpers/commitHelper'
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
import { ApolloServer } from 'apollo-server-express'
import { expect } from 'chai'
describe('Commits (GraphQL)', () => {
@@ -94,10 +94,13 @@ describe('Commits (GraphQL)', () => {
})
describe('when user authenticated', async () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildAuthenticatedApolloServer(me.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
})
describe('and reading user commits', async () => {
@@ -1,3 +1,4 @@
import { buildApolloServer } from '@/app'
import { Streams, Users } from '@/modules/core/dbSchema'
import { getStream, setStreamFavorited } from '@/modules/core/repositories/streams'
import { Nullable, Optional } from '@/modules/shared/helpers/typeHelper'
@@ -11,14 +12,14 @@ import {
readDiscoverableStreams,
updateStream
} from '@/test/graphql/streams'
import { truncateTables } from '@/test/hooks'
import {
buildAuthenticatedApolloServer,
buildUnauthenticatedApolloServer
} from '@/test/serverHelper'
createAuthedTestContext,
createTestContext,
ServerAndContext
} from '@/test/graphqlHelper'
import { truncateTables } from '@/test/hooks'
import { BasicTestStream, createTestStream } from '@/test/speckle-helpers/streamHelper'
import { wait } from '@speckle/shared'
import { ApolloServer } from 'apollo-server-express'
import { expect } from 'chai'
import dayjs from 'dayjs'
import { shuffle } from 'lodash'
@@ -28,7 +29,7 @@ const READABLE_DISCOVERABLE_STREAM_COUNT = 15
const cleanup = async () => await truncateTables([Streams.name, Users.name])
describe('Discoverable streams', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
const me: BasicTestUser = {
name: 'itsaa meeee',
@@ -111,7 +112,10 @@ describe('Discoverable streams', () => {
}
}
apollo = await buildUnauthenticatedApolloServer()
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
})
after(async () => {
@@ -234,10 +238,13 @@ describe('Discoverable streams', () => {
})
describe('when authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildAuthenticatedApolloServer(me.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
})
it('can be retrieved with role properly filled out', async () => {
@@ -3,13 +3,16 @@ const expect = require('chai').expect
const { buildApolloServer } = require('@/app')
const { StreamFavorites, Streams, Users } = require('@/modules/core/dbSchema')
const { Roles, AllScopes } = require('@/modules/core/helpers/mainConstants')
const { createStream } = require('@/modules/core/services/streams')
const { createUser } = require('@/modules/core/services/users')
const { addLoadersToCtx } = require('@/modules/shared/middleware')
const { truncateTables } = require('@/test/hooks')
const { gql } = require('apollo-server-express')
const { gql } = require('graphql-tag')
const { sleep } = require('@/test/helpers')
const {
createAuthedTestContext,
createTestContext,
executeOperation
} = require('@/test/graphqlHelper')
/**
* Cleaning up relevant tables
@@ -140,26 +143,17 @@ describe('Favorite streams', () => {
})
describe('when authenticated', () => {
/** @type {import('apollo-server-express').ApolloServer} */
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
let apollo
const favoriteStream = async (sid, favorited) =>
await apollo.executeOperation({
query: favoriteMutationGql,
variables: { sid, favorited }
})
await executeOperation(apollo, favoriteMutationGql, { sid, favorited })
before(async () => {
apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: me.id,
role: Roles.Server.User,
token: 'asd',
scopes: AllScopes
})
})
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
// Drop all favorites to ensure we don't favorite already favorited streams
await StreamFavorites.knex().truncate()
@@ -242,10 +236,7 @@ describe('Favorite streams', () => {
]
const getFavorites = async (cursor, limit = 10) =>
await apollo.executeOperation({
query: favoriteStreamsQueryGql,
variables: { cursor, limit }
})
await executeOperation(apollo, favoriteStreamsQueryGql, { cursor, limit })
const favoritedStreamIds = () => favoritableStreams.map((s) => s.id)
@@ -267,10 +258,11 @@ describe('Favorite streams', () => {
})
it("throw error if trying to get another user's favorite stream collection", async () => {
const { data, errors } = await apollo.executeOperation({
query: anotherUserFavoriteStreamsQueryGql,
variables: { limit: 10, uid: otherGuy.id }
})
const { data, errors } = await executeOperation(
apollo,
anotherUserFavoriteStreamsQueryGql,
{ limit: 10, uid: otherGuy.id }
)
expect(data).to.be.ok
expect(data.otherUser?.favoriteStreams).to.not.be.ok
@@ -327,23 +319,16 @@ describe('Favorite streams', () => {
oldNewQueryDataset.forEach(({ display, isNew }) => {
it(`return total favorites count for user (${display} query)`, async () => {
// "Log in" with other user
const apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: otherGuy.id,
role: Roles.Server.User,
token: 'asd',
scopes: AllScopes
})
})
const apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(otherGuy.id)
}
const { data, errors } = await apollo.executeOperation({
query: isNew
? totalOwnedStreamsFavoritesNew
: totalOwnedStreamsFavoritesOld,
variables: { uid: me.id }
})
const { data, errors } = await executeOperation(
apollo,
isNew ? totalOwnedStreamsFavoritesNew : totalOwnedStreamsFavoritesOld,
{ uid: me.id }
)
expect(errors).to.not.be.ok
@@ -356,19 +341,20 @@ describe('Favorite streams', () => {
})
describe('when not authenticated', () => {
/** @type {import('apollo-server-express').ApolloServer} */
/** @type {import('@/test/graphqlHelper').ServerAndContext} */
let apollo
before(async () => {
apollo = await buildApolloServer({
context: () => ({})
})
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
})
it("can't be favorited", async () => {
const result = await apollo.executeOperation({
query: favoriteMutationGql,
variables: { sid: myPubStream.id, favorited: true }
const result = await executeOperation(apollo, favoriteMutationGql, {
sid: myPubStream.id,
favorited: true
})
expect(result.data.streamFavorite).to.not.be.ok
@@ -377,9 +363,7 @@ describe('Favorite streams', () => {
})
it("can't be retrieved", async () => {
const result = await apollo.executeOperation({
query: favoriteStreamsQueryGql
})
const result = await executeOperation(apollo, favoriteStreamsQueryGql)
expect(result.data.activeUser).to.be.null
expect(result.errors).to.not.be.ok
@@ -15,9 +15,9 @@ const { createUser } = require('@/modules/core/services/users')
const { validateScopes, authorizeResolver } = require('@/modules/shared')
const { buildContext } = require('@/modules/shared/middleware')
const { ForbiddenError } = require('apollo-server-express')
const { Roles, Scopes } = require('@speckle/shared')
const { throwForNotHavingServerRole } = require('@/modules/shared/authz')
const { ForbiddenError } = require('@/modules/shared/errors')
describe('Generic AuthN & AuthZ controller tests', () => {
before(async () => {
@@ -303,7 +303,7 @@ describe('GraphQL API Core @core-api', () => {
})
expect(goodTokenScopesBadEmail.body.errors).to.exist
expect(goodTokenScopesBadEmail.body.errors[0].extensions?.code).to.equal(
'BAD_USER_INPUT'
'BAD_REQUEST_ERROR'
)
const goodTokenScopesGoodEmail = await sendRequest(userDelete.token, {
query:
@@ -626,7 +626,9 @@ describe('GraphQL API Core @core-api', () => {
query: '{ adminStreams(limit: 200) { totalCount items { id name } } }'
})
expect(streamResults.body.errors).to.exist
expect(streamResults.body.errors[0].extensions.code).to.equal('BAD_USER_INPUT')
expect(streamResults.body.errors[0].extensions.code).to.equal(
'BAD_REQUEST_ERROR'
)
streamResults = await sendRequest(userA.token, {
query: '{ adminStreams(limit: 2) { totalCount items { id name } } }'
@@ -1269,14 +1271,14 @@ describe('GraphQL API Core @core-api', () => {
let res = await sendRequest(userB.token, { query: queryLim })
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('BAD_USER_INPUT')
expect(res.body.errors[0].extensions.code).to.equal('BAD_REQUEST_ERROR')
const queryPagination =
'query { userSearch( query: "matteo", limit: 200 ) { cursor items { id name } } } '
res = await sendRequest(userB.token, { query: queryPagination })
expect(res).to.be.json
expect(res.body.errors).to.exist
expect(res.body.errors[0].extensions.code).to.equal('BAD_USER_INPUT')
expect(res.body.errors[0].extensions.code).to.equal('BAD_REQUEST_ERROR')
})
})
@@ -1,6 +1,6 @@
const expect = require('chai').expect
const request = require('supertest')
const { gql } = require('apollo-server-express')
const { gql } = require('graphql-tag')
const { WebSocketLink } = require('@apollo/client/link/ws')
const { execute } = require('@apollo/client/core')
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import { gql } from 'graphql-tag'
export const createObjectMutation = gql`
mutation CreateObject($input: ObjectCreateInput!) {
@@ -22,10 +22,6 @@ import {
isStreamCollaborator
} from '@/modules/core/services/streams/streamAccessService'
import { Roles } from '@/modules/core/helpers/mainConstants'
import {
buildAuthenticatedApolloServer,
buildUnauthenticatedApolloServer
} from '@/test/serverHelper'
import {
getLimitedUserStreams,
getUserStreams,
@@ -43,7 +39,6 @@ import {
} from '@/modules/core/repositories/streams'
import { has, times } from 'lodash'
import { Streams } from '@/modules/core/dbSchema'
import { ApolloServer } from 'apollo-server-express'
import { Nullable } from '@/modules/shared/helpers/typeHelper'
import { sleep } from '@/test/helpers'
import dayjs, { Dayjs } from 'dayjs'
@@ -53,6 +48,12 @@ import {
} from '@/test/graphql/generated/graphql'
import { Get } from 'type-fest'
import { changeUserRole } from '@/modules/core/services/users'
import {
createAuthedTestContext,
createTestContext,
ServerAndContext
} from '@/test/graphqlHelper'
import { buildApolloServer } from '@/app'
describe('Streams @core-streams', () => {
const userOne: BasicTestUser = {
@@ -210,7 +211,10 @@ describe('Streams @core-streams', () => {
userOne.id
)
const apollo = await buildAuthenticatedApolloServer(userTwo.id)
const apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(userTwo.id)
}
const { data, errors } = await leaveStream(apollo, { streamId })
expect(errors).to.be.not.ok
@@ -445,7 +449,7 @@ describe('Streams @core-streams', () => {
* Base test for testing paginated & unpaginated User.streams query in various circumstances
*/
const testPaginatedUserStreams = async (
apollo: ApolloServer,
apollo: ServerAndContext,
pagination: boolean,
userId: string,
isOtherUser: boolean,
@@ -534,12 +538,15 @@ describe('Streams @core-streams', () => {
}
describe('and user is authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
let activeUserId: string
before(async () => {
activeUserId = userOne.id
apollo = await buildAuthenticatedApolloServer(activeUserId)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(activeUserId)
}
})
paginationDataset.forEach(({ display, pagination }) => {
@@ -562,10 +569,13 @@ describe('Streams @core-streams', () => {
})
describe('and user is not authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildUnauthenticatedApolloServer()
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
})
userLimitedUserDataSet.forEach(({ display, limitedUser }) => {
@@ -573,8 +583,17 @@ describe('Streams @core-streams', () => {
const results = limitedUser
? await getLimitedUserStreams(apollo, { userId: userOne.id })
: await getUserStreams(apollo, { userId: userOne.id })
const user = results.data
? 'otherUser' in results.data
? results.data.otherUser
: 'user' in results.data
? results.data.user
: null
: null
expect(results).to.haveGraphQLErrors()
expect(results.data?.otherUser || results.data?.user).to.be.not.ok
expect(user).to.be.not.ok
})
})
})
@@ -6,12 +6,11 @@ import { times, clamp } from 'lodash'
import { createStreamInviteDirectly } from '@/test/speckle-helpers/inviteHelper'
import { getAdminUsersList } from '@/test/graphql/users'
import { buildApolloServer } from '@/app'
import { addLoadersToCtx } from '@/modules/shared/middleware'
import { Roles, AllScopes } from '@/modules/core/helpers/mainConstants'
import { Roles } from '@/modules/core/helpers/mainConstants'
import { expect } from 'chai'
import { ApolloServer } from 'apollo-server-express'
import { Optional } from '@/modules/shared/helpers/typeHelper'
import { wait } from '@speckle/shared'
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
// To ensure that the invites are created in the correct order, we need to wait a bit between each creation
const WAIT_TIMEOUT = 5
@@ -59,7 +58,7 @@ describe('[Admin users list]', () => {
const totalCount = USER_COUNT + SERVER_INVITE_COUNT + STREAM_INVITE_COUNT
const totalInviteCount = SERVER_INVITE_COUNT + STREAM_INVITE_COUNT
let apollo: ApolloServer
let apollo: ServerAndContext
let orderedUserIds: string[] = []
let orderedInviteIds: string[] = []
@@ -176,16 +175,10 @@ describe('[Admin users list]', () => {
orderedInviteIds = await getOrderedInviteIds()
orderedUserIds = await getOrderedUserIds()
apollo = await buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId: me.id,
role: Roles.Server.Admin,
token: 'asd',
scopes: AllScopes
})
})
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id!, { role: Roles.Server.Admin })
}
})
after(async () => {
@@ -2,11 +2,6 @@ import { Users } from '@/modules/core/dbSchema'
import { BasicTestUser, createTestUsers } from '@/test/authHelper'
import { getActiveUser, getOtherUser } from '@/test/graphql/users'
import { beforeEachContext, truncateTables } from '@/test/hooks'
import {
buildAuthenticatedApolloServer,
buildUnauthenticatedApolloServer
} from '@/test/serverHelper'
import { ApolloServer } from 'apollo-server-express'
import { expect } from 'chai'
import { createUser } from '@/modules/core/services/users'
import {
@@ -20,7 +15,12 @@ import {
} from '@/modules/core/repositories/userEmails'
import { db } from '@/db/knex'
import { before } from 'mocha'
import { testApolloServer } from '@/test/graphqlHelper'
import {
createAuthedTestContext,
createTestContext,
ServerAndContext,
testApolloServer
} from '@/test/graphqlHelper'
import { GetActiveUserEmailsDocument } from '@/test/graphql/generated/graphql'
import { validateAndCreateUserEmailFactory } from '@/modules/core/services/userEmails'
import { finalizeInvitedServerRegistrationFactory } from '@/modules/serverinvites/services/processing'
@@ -29,6 +29,7 @@ import {
updateAllInviteTargetsFactory
} from '@/modules/serverinvites/repositories/serverInvites'
import { requestNewEmailVerification } from '@/modules/emails/services/verification/request'
import { buildApolloServer } from '@/app'
const createUserEmail = validateAndCreateUserEmailFactory({
createUserEmail: createUserEmailFactory({ db }),
@@ -64,10 +65,13 @@ describe('Users (GraphQL)', () => {
})
describe('when unauthenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildUnauthenticatedApolloServer()
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
})
it('activeUser returns null', async () => {
@@ -88,10 +92,13 @@ describe('Users (GraphQL)', () => {
})
describe('when authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildAuthenticatedApolloServer(me.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(me.id)
}
})
it('activeUser returns authenticated user info', async () => {
@@ -1,11 +1,6 @@
import { EmailVerifications, UserEmails, Users } from '@/modules/core/dbSchema'
import { BasicTestUser, createTestUser, createTestUsers } from '@/test/authHelper'
import { buildApp, truncateTables } from '@/test/hooks'
import {
buildAuthenticatedApolloServer,
buildUnauthenticatedApolloServer
} from '@/test/serverHelper'
import { ApolloServer } from 'apollo-server-express'
import request from 'supertest'
import { expect } from 'chai'
import { deleteVerifications, getPendingToken } from '@/modules/emails/repositories'
@@ -19,6 +14,12 @@ import { Express } from 'express'
import { getUser } from '@/modules/core/repositories/users'
import dayjs from 'dayjs'
import { EmailSendingServiceMock } from '@/test/mocks/global'
import {
createAuthedTestContext,
createTestContext,
ServerAndContext
} from '@/test/graphqlHelper'
import { buildApolloServer } from '@/app'
const mailerMock = EmailSendingServiceMock
@@ -71,10 +72,13 @@ describe('Email verifications @emails', () => {
})
describe('when authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildAuthenticatedApolloServer(userA.id)
apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(userA.id)
}
})
it('pending verification is reported correctly', async () => {
@@ -101,7 +105,10 @@ describe('Email verifications @emails', () => {
describe('and requesting verification', () => {
const invokeRequestVerification = async (user: BasicTestUser) => {
const apollo = await buildAuthenticatedApolloServer(user.id)
const apollo = {
apollo: await buildApolloServer(),
context: createAuthedTestContext(user.id)
}
return await requestVerification(apollo, {})
}
@@ -147,10 +154,13 @@ describe('Email verifications @emails', () => {
})
describe('when not authenticated', () => {
let apollo: ApolloServer
let apollo: ServerAndContext
before(async () => {
apollo = await buildUnauthenticatedApolloServer()
apollo = {
apollo: await buildApolloServer(),
context: createTestContext()
}
})
it('cant request an account verification', async () => {
@@ -57,7 +57,7 @@ describe('FileUploads @fileuploads', () => {
after(async () => {
process.env['CANONICAL_URL'] = existingCanonicalUrl
await server.close()
await server?.close()
})
describe('Uploads files', () => {
+1 -1
View File
@@ -141,7 +141,7 @@ exports.graphDataloadersBuilders = () => {
/**
* GQL components will be loaded even from disabled modules to avoid schema complexity, so ensure
* that resolvers return valid values even if the module is disabled
* @returns {Pick<import('apollo-server-express').Config, 'resolvers' | 'typeDefs'> & { directiveBuilders: Record<string, import('@/modules/core/graph/helpers/directiveHelper').GraphqlDirectiveBuilder>}}
* @returns {Pick<import('@apollo/server').ApolloServerOptions, 'resolvers' | 'typeDefs'> & { directiveBuilders: Record<string, import('@/modules/core/graph/helpers/directiveHelper').GraphqlDirectiveBuilder>}}
*/
const graphComponents = () => {
// Base query and mutation to allow for type extension by modules.
+1 -1
View File
@@ -83,7 +83,7 @@ const buildBaseConfig = async (): Promise<SpeckleModuleMocksConfig> => {
/**
* Define mocking config in dev env
* https://www.apollographql.com/docs/apollo-server/v3/testing/mocking
* https://www.apollographql.com/docs/apollo-server/testing/mocking
*/
export async function buildMocksConfig(): Promise<{
mocks: boolean | IMocks
@@ -227,7 +227,7 @@ describe('[Stream & Server Invites]', () => {
streamId: otherGuyAlreadyInvitedStream.id
})
expect(data?.serverInviteCreate).to.be.not.ok
expect(data?.streamInviteCreate).to.be.not.ok
expect(errors).to.be.ok
expect(errors!.map((e) => e.message).join('|')).to.contain(
'user is already a collaborator'
@@ -2,6 +2,15 @@ import { AuthContext } from '@/modules/shared/authz'
import { base64Decode, base64Encode } from '@/modules/shared/helpers/cryptoHelper'
import DataLoader from 'dataloader'
import dayjs, { Dayjs } from 'dayjs'
import { ApolloServerErrorCode } from '@apollo/server/errors'
import { GraphQLError } from 'graphql'
import {
BadRequestError,
ForbiddenError,
InvalidArgumentError,
NotFoundError,
UnauthorizedError
} from '@/modules/shared/errors'
/**
* Encode cursor to turn it into an opaque & obfuscated value
@@ -65,3 +74,25 @@ export const defineRequestDataloaders = <
): RequestDataLoadersBuilder<T> => {
return builder
}
/**
* Is a lower significance error, caused by user error (and thus - not a bug in our code)
*/
export const isUserGraphqlError = (error: GraphQLError): boolean => {
const userCodes = [
ForbiddenError.code,
UnauthorizedError.code,
BadRequestError.code,
NotFoundError.code,
InvalidArgumentError.code,
ApolloServerErrorCode.BAD_REQUEST,
ApolloServerErrorCode.BAD_USER_INPUT,
ApolloServerErrorCode.GRAPHQL_PARSE_FAILED,
ApolloServerErrorCode.GRAPHQL_VALIDATION_FAILED,
ApolloServerErrorCode.OPERATION_RESOLUTION_FAILURE,
ApolloServerErrorCode.PERSISTED_QUERY_NOT_FOUND,
ApolloServerErrorCode.PERSISTED_QUERY_NOT_SUPPORTED
]
const code = error.extensions?.code as string
return userCodes.includes(code)
}
@@ -10,6 +10,7 @@ import { AuthContext } from '@/modules/shared/authz'
import { Express } from 'express'
import { ConditionalKeys, SetRequired } from 'type-fest'
import pino from 'pino'
import { BaseContext } from '@apollo/server'
export type MarkNullableOptional<T> = SetRequired<
Partial<T>,
@@ -40,14 +41,15 @@ export type SpeckleModule<T extends Record<string, unknown> = Record<string, unk
shutdown?: () => MaybeAsync<void>
} & T
export type GraphQLContext = AuthContext & {
/**
* Request-scoped GraphQL dataloaders
* @see https://github.com/graphql/dataloader
*/
loaders: RequestDataLoaders
export type GraphQLContext = BaseContext &
AuthContext & {
/**
* Request-scoped GraphQL dataloaders
* @see https://github.com/graphql/dataloader
*/
loaders: RequestDataLoaders
log: pino.Logger
}
log: pino.Logger
}
export { Nullable, Optional, MaybeNullOrUndefined, MaybeAsync, MaybeFalsy }
+1 -2
View File
@@ -1,6 +1,4 @@
'use strict'
const knex = require(`@/db/knex`)
const { ForbiddenError } = require('apollo-server-express')
const {
pubsub,
StreamSubscriptions,
@@ -17,6 +15,7 @@ const {
isResourceAllowed
} = require('@/modules/core/helpers/token')
const db = require('@/db/knex')
const { ForbiddenError } = require('@/modules/shared/errors')
const ServerAcl = () => ServerAclSchema.knex()
/**
@@ -156,7 +156,7 @@ export async function buildContext({
cleanLoadersEarly
}: {
req: MaybeNullOrUndefined<Request>
token: Nullable<string>
token?: Nullable<string>
cleanLoadersEarly?: boolean
}): Promise<GraphQLContext> {
const ctx =
@@ -1,5 +1,3 @@
const { ForbiddenError } = require('apollo-server-express')
const { authorizeResolver } = require('@/modules/shared')
const {
deleteWebhook,
@@ -10,6 +8,7 @@ const {
const { Roles } = require('@speckle/shared')
const { getWebhookByIdFactory } = require('../../repositories/webhooks')
const { db } = require('@/db/knex')
const { ForbiddenError } = require('@/modules/shared/errors')
const streamWebhooksResolver = async (parent, args, context) => {
await authorizeResolver(
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
import { GraphqlDirectiveBuilder } from '@/modules/core/graph/helpers/directiveHelper'
import { authorizeResolver } from '@/modules/shared'
import { ForbiddenError } from '@/modules/shared/errors'
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import { gql } from 'graphql-tag'
export const basicWorkspaceFragment = gql`
fragment BasicWorkspace on Workspace {
@@ -56,7 +56,6 @@ import {
createTestStreams,
leaveStream
} from '@/test/speckle-helpers/streamHelper'
import { ForbiddenError } from 'apollo-server-express'
import { Workspaces } from '@/modules/workspaces/helpers/db'
import {
generateRegistrationParams,
@@ -78,6 +77,7 @@ import { markUserEmailAsVerifiedFactory } from '@/modules/core/services/users/em
import { createRandomPassword } from '@/modules/core/helpers/testHelpers'
import { addOrUpdateStreamCollaborator } from '@/modules/core/services/streams/streamAccessService'
import { WorkspaceProtectedError } from '@/modules/workspaces/errors/workspace'
import { ForbiddenError } from '@/modules/shared/errors'
enum InviteByTarget {
Email = 'email',
+6 -4
View File
@@ -38,15 +38,16 @@
},
"dependencies": {
"@apollo/client": "^3.7.0",
"@apollo/server": "^4.11.0",
"@aws-sdk/client-s3": "^3.276.0",
"@aws-sdk/lib-storage": "^3.100.0",
"@godaddy/terminus": "^4.9.0",
"@graphql-tools/schema": "^10.0.4",
"@graphql-tools/mock": "^9.0.4",
"@graphql-tools/schema": "^10.0.6",
"@mailchimp/mailchimp_marketing": "^3.0.80",
"@speckle/objectloader": "workspace:^",
"@speckle/shared": "workspace:^",
"ajv": "^8.12.0",
"apollo-server-express": "^3.10.2",
"bcrypt": "^5.0.0",
"bull": "^4.8.5",
"busboy": "^1.4.0",
@@ -65,10 +66,10 @@
"express-async-errors": "^3.1.1",
"express-prom-bundle": "^6.6.0",
"express-session": "^1.17.1",
"graphql": "^15",
"graphql-redis-subscriptions": "^2.2.2",
"graphql-scalars": "^1.18.0",
"graphql-subscriptions": "^2.0.0",
"graphql-tag": "^2.12.6",
"ioredis": "^5.2.2",
"jose": "^5.6.3",
"knex": "^2.4.1",
@@ -129,6 +130,7 @@
"@types/compression": "^1.7.2",
"@types/connect-redis": "^0.0.23",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/debug": "^4.1.7",
"@types/deep-equal-in-any-order": "^1.0.1",
"@types/ejs": "^3.1.1",
@@ -159,7 +161,6 @@
"@types/zxcvbn": "^4.4.1",
"@typescript-eslint/eslint-plugin": "^5.39.0",
"@typescript-eslint/parser": "^5.39.0",
"apollo-server-plugin-base": "^3.7.2",
"axios": "^1.7.4",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.2",
@@ -170,6 +171,7 @@
"enforce-unique": "^1.3.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"graphql": "^16.6.0",
"http-proxy-middleware": "v3.0.0-beta.0",
"ioredis-mock": "^8.9.0",
"mocha": "^10.1.0",
@@ -10,8 +10,8 @@ import {
UseStreamAccessRequestMutation,
UseStreamAccessRequestMutationVariables
} from '@/test/graphql/generated/graphql'
import { executeOperation } from '@/test/graphqlHelper'
import { ApolloServer, gql } from 'apollo-server-express'
import { executeOperation, ExecuteOperationServer } from '@/test/graphqlHelper'
import { gql } from 'graphql-tag'
const basicStreamAccessRequestFragment = gql`
fragment BasicStreamAccessRequestFields on StreamAccessRequest {
@@ -89,7 +89,7 @@ const useStreamAccessRequestMutation = gql`
`
export const createStreamAccessRequest = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: CreateStreamAccessRequestMutationVariables
) =>
executeOperation<
@@ -98,7 +98,7 @@ export const createStreamAccessRequest = (
>(apollo, createStreamAccessRequestMutation, variables)
export const getStreamAccessRequest = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetStreamAccessRequestQueryVariables
) =>
executeOperation<GetStreamAccessRequestQuery, GetStreamAccessRequestQueryVariables>(
@@ -108,7 +108,7 @@ export const getStreamAccessRequest = (
)
export const getFullStreamAccessRequest = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetFullStreamAccessRequestQueryVariables
) =>
executeOperation<
@@ -117,7 +117,7 @@ export const getFullStreamAccessRequest = (
>(apollo, getFullStreamAccessRequestQuery, variables)
export const getPendingStreamAccessRequests = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetPendingStreamAccessRequestsQueryVariables
) =>
executeOperation<
@@ -126,7 +126,7 @@ export const getPendingStreamAccessRequests = (
>(apollo, getPendingStreamAccessRequestsQuery, variables)
export const useStreamAccessRequest = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: UseStreamAccessRequestMutationVariables
) =>
executeOperation<
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import { gql } from 'graphql-tag'
export const createTokenMutation = gql`
mutation CreateToken($token: ApiTokenCreateInput!) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import { gql } from 'graphql-tag'
export const automateFunctionFragment = gql`
fragment TestAutomateFunction on AutomateFunction {
+9 -6
View File
@@ -8,8 +8,8 @@ import {
GetCommentsQuery,
GetCommentsQueryVariables
} from '@/test/graphql/generated/graphql'
import { executeOperation } from '@/test/graphqlHelper'
import { ApolloServer, gql } from 'apollo-server-express'
import { executeOperation, ExecuteOperationServer } from '@/test/graphqlHelper'
import { gql } from 'graphql-tag'
const commentWithRepliesFragment = gql`
fragment CommentWithReplies on Comment {
@@ -76,7 +76,7 @@ const getCommentsQuery = gql`
`
export const createComment = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: CreateCommentMutationVariables
) =>
executeOperation<CreateCommentMutation, CreateCommentMutationVariables>(
@@ -86,7 +86,7 @@ export const createComment = (
)
export const createReply = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: CreateReplyMutationVariables
) =>
executeOperation<CreateReplyMutation, CreateReplyMutationVariables>(
@@ -95,7 +95,10 @@ export const createReply = (
variables
)
export const getComment = (apollo: ApolloServer, variables: GetCommentQueryVariables) =>
export const getComment = (
apollo: ExecuteOperationServer,
variables: GetCommentQueryVariables
) =>
executeOperation<GetCommentQuery, GetCommentQueryVariables>(
apollo,
getCommentQuery,
@@ -103,7 +106,7 @@ export const getComment = (apollo: ApolloServer, variables: GetCommentQueryVaria
)
export const getComments = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetCommentsQueryVariables
) =>
executeOperation<GetCommentsQuery, GetCommentsQueryVariables>(
+7 -7
View File
@@ -10,8 +10,8 @@ import {
ReadStreamBranchCommitsQuery,
ReadStreamBranchCommitsQueryVariables
} from '@/test/graphql/generated/graphql'
import { executeOperation } from '@/test/graphqlHelper'
import { ApolloServer, gql } from 'apollo-server-express'
import { executeOperation, ExecuteOperationServer } from '@/test/graphqlHelper'
import gql from 'graphql-tag'
const baseCommitFieldsFragment = gql`
fragment BaseCommitFields on Commit {
@@ -108,7 +108,7 @@ const deleteCommitsMutation = gql`
`
export const readOwnCommits = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: ReadOwnCommitsQueryVariables
) =>
executeOperation<ReadOwnCommitsQuery, ReadOwnCommitsQueryVariables>(
@@ -118,7 +118,7 @@ export const readOwnCommits = (
)
export const readOtherUsersCommits = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: ReadOtherUsersCommitsQueryVariables
) =>
executeOperation<ReadOtherUsersCommitsQuery, ReadOtherUsersCommitsQueryVariables>(
@@ -128,7 +128,7 @@ export const readOtherUsersCommits = (
)
export const readStreamBranchCommits = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: ReadStreamBranchCommitsQueryVariables
) =>
executeOperation<ReadStreamBranchCommitsQuery, ReadStreamBranchCommitsQueryVariables>(
@@ -138,7 +138,7 @@ export const readStreamBranchCommits = (
)
export const moveCommits = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: MoveCommitsMutationVariables
) =>
executeOperation<MoveCommitsMutation, MoveCommitsMutationVariables>(
@@ -148,7 +148,7 @@ export const moveCommits = (
)
export const deleteCommits = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: DeleteCommitsMutationVariables
) =>
executeOperation<DeleteCommitsMutation, DeleteCommitsMutationVariables>(
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const createProjectModelQuery = gql`
mutation CreateProjectModel($input: CreateModelInput!) {
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
const basicProjectAccessRequestFragment = gql`
fragment BasicProjectAccessRequestFields on ProjectAccessRequest {
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const createProjectCommentMutation = gql`
mutation CreateProjectComment($input: CreateCommentInput!) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const basicProjectFieldsFragment = gql`
fragment BasicProjectFields on Project {
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const createServerInviteMutation = gql`
mutation CreateServerInvite($input: ServerInviteCreateInput!) {
+12 -9
View File
@@ -14,8 +14,8 @@ import {
GetLimitedUserStreamsQuery,
GetLimitedUserStreamsQueryVariables
} from '@/test/graphql/generated/graphql'
import { executeOperation } from '@/test/graphqlHelper'
import { ApolloServer, gql } from 'apollo-server-express'
import { executeOperation, ExecuteOperationServer } from '@/test/graphqlHelper'
import gql from 'graphql-tag'
export const basicStreamFieldsFragment = gql`
fragment BasicStreamFields on Stream {
@@ -129,7 +129,7 @@ const getLimitedUserStreamsQuery = gql`
`
export const leaveStream = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: LeaveStreamMutationVariables
) =>
executeOperation<LeaveStreamMutation, LeaveStreamMutationVariables>(
@@ -139,7 +139,7 @@ export const leaveStream = (
)
export const createStream = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: CreateStreamMutationVariables
) =>
executeOperation<CreateStreamMutation, CreateStreamMutationVariables>(
@@ -149,7 +149,7 @@ export const createStream = (
)
export const updateStream = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: UpdateStreamMutationVariables
) =>
executeOperation<UpdateStreamMutation, UpdateStreamMutationVariables>(
@@ -158,7 +158,10 @@ export const updateStream = (
variables
)
export const readStream = (apollo: ApolloServer, variables: ReadStreamQueryVariables) =>
export const readStream = (
apollo: ExecuteOperationServer,
variables: ReadStreamQueryVariables
) =>
executeOperation<ReadStreamQuery, ReadStreamQueryVariables>(
apollo,
readStreamQuery,
@@ -166,7 +169,7 @@ export const readStream = (apollo: ApolloServer, variables: ReadStreamQueryVaria
)
export const readDiscoverableStreams = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: ReadDiscoverableStreamsQueryVariables
) =>
executeOperation<ReadDiscoverableStreamsQuery, ReadDiscoverableStreamsQueryVariables>(
@@ -176,7 +179,7 @@ export const readDiscoverableStreams = (
)
export const getUserStreams = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetUserStreamsQueryVariables
) =>
executeOperation<GetUserStreamsQuery, GetUserStreamsQueryVariables>(
@@ -186,7 +189,7 @@ export const getUserStreams = (
)
export const getLimitedUserStreams = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetLimitedUserStreamsQueryVariables
) =>
executeOperation<GetLimitedUserStreamsQuery, GetLimitedUserStreamsQueryVariables>(
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const userWithEmailsFragment = gql`
fragment UserWithEmails on User {
+7 -7
View File
@@ -1,4 +1,3 @@
import { ApolloServer, gql } from 'apollo-server-express'
import {
GetActiveUserQuery,
GetActiveUserQueryVariables,
@@ -11,7 +10,8 @@ import {
RequestVerificationMutation,
RequestVerificationMutationVariables
} from '@/test/graphql/generated/graphql'
import { executeOperation } from '@/test/graphqlHelper'
import { executeOperation, ExecuteOperationServer } from '@/test/graphqlHelper'
import gql from 'graphql-tag'
const baseUserFieldsFragment = gql`
fragment BaseUserFields on User {
@@ -99,14 +99,14 @@ const requestVerificationMutation = gql`
}
`
export const getActiveUser = (apollo: ApolloServer) =>
export const getActiveUser = (apollo: ExecuteOperationServer) =>
executeOperation<GetActiveUserQuery, GetActiveUserQueryVariables>(
apollo,
getActiveUserQuery
)
export const getOtherUser = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetOtherUserQueryVariables
) =>
executeOperation<GetOtherUserQuery, GetOtherUserQueryVariables>(
@@ -116,7 +116,7 @@ export const getOtherUser = (
)
export async function getAdminUsersList(
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetAdminUsersQueryVariables
) {
return await executeOperation<GetAdminUsersQuery, GetAdminUsersQueryVariables>(
@@ -127,7 +127,7 @@ export async function getAdminUsersList(
}
export const getPendingEmailVerificationStatus = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables: GetPendingEmailVerificationStatusQueryVariables
) =>
executeOperation<
@@ -136,7 +136,7 @@ export const getPendingEmailVerificationStatus = (
>(apollo, getPendingEmailVerificationStatusQuery, variables)
export const requestVerification = (
apollo: ApolloServer,
apollo: ExecuteOperationServer,
variables?: RequestVerificationMutationVariables
) =>
executeOperation<RequestVerificationMutation, RequestVerificationMutationVariables>(
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const createProjectVersionMutation = gql`
mutation CreateProjectVersion($input: CreateVersionInput!) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { gql } from 'apollo-server-express'
import gql from 'graphql-tag'
export const workspaceFragment = gql`
fragment TestWorkspace on Workspace {
+95 -41
View File
@@ -1,36 +1,77 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ApolloServer } from 'apollo-server-express'
import { GraphQLResponse } from 'apollo-server-types'
import { DocumentNode } from 'graphql'
import { GraphQLContext, Nullable } from '@/modules/shared/helpers/typeHelper'
import { GraphQLContext } from '@/modules/shared/helpers/typeHelper'
import { TypedDocumentNode } from '@graphql-typed-document-node/core'
import { buildApolloServer } from '@/app'
import { addLoadersToCtx } from '@/modules/shared/middleware'
import { buildUnauthenticatedApolloServer } from '@/test/serverHelper'
import { Roles } from '@/modules/core/helpers/mainConstants'
import { AllScopes } from '@speckle/shared'
import { AllScopes, MaybeNullOrUndefined } from '@speckle/shared'
import { expect } from 'chai'
import { ApolloServer, GraphQLResponse } from '@apollo/server'
type TypedGraphqlResponse<R = Record<string, any>> = GraphQLResponse & {
data: Nullable<R>
type TypedGraphqlResponse<R = Record<string, any>> = GraphQLResponse<R>
export const getResponseResults = <TData = Record<string, unknown>>(
res: GraphQLResponse<TData>
) => {
const body = res.body
if (body.kind === 'incremental') {
return {
data: body.initialResult.data as MaybeNullOrUndefined<TData>,
errors: body.initialResult.errors
}
} else {
return {
data: body.singleResult.data as MaybeNullOrUndefined<TData>,
errors: body.singleResult.errors
}
}
}
export type ExecuteOperationResponse<R extends Record<string, any>> = {
res: TypedGraphqlResponse<R>
} & ReturnType<typeof getResponseResults<R>>
export type ServerAndContext = {
apollo: ApolloServer<GraphQLContext>
context?: MaybeNullOrUndefined<GraphQLContext>
}
export type ExecuteOperationServer = ServerAndContext
/**
* Use this to execute GQL operations from tests against an Apollo instance and get
* a properly typed response
* @deprecated Use `testApolloServer` instead
*/
export async function executeOperation<
R extends Record<string, any> = Record<string, any>,
V extends Record<string, any> = Record<string, any>
>(
apollo: ApolloServer,
apollo: ExecuteOperationServer,
query: DocumentNode,
variables?: V
): Promise<TypedGraphqlResponse<R>> {
return (await apollo.executeOperation({
query,
variables
})) as TypedGraphqlResponse<R>
variables?: V,
context?: GraphQLContext
): Promise<ExecuteOperationResponse<R>> {
const server: ApolloServer<GraphQLContext> = apollo.apollo
const contextValue = context || apollo.context || createTestContext()
const res = (await server.executeOperation(
{
query,
variables
},
{ contextValue }
)) as TypedGraphqlResponse<R>
const results = getResponseResults(res)
// Replicate clearing dataloaders after each request
contextValue.loaders.clearAll()
return {
...results,
res
}
}
/**
@@ -51,6 +92,19 @@ export const createTestContext = (
...(ctx || {})
})
export const createAuthedTestContext = (
userId: string,
ctxOverrides?: Partial<Parameters<typeof addLoadersToCtx>[0]>
): GraphQLContext =>
addLoadersToCtx({
auth: true,
userId,
role: Roles.Server.User,
token: 'asd',
scopes: AllScopes,
...(ctxOverrides || {})
})
/**
* Utilities that make it easier to test against an Apollo Server instance
*/
@@ -61,21 +115,16 @@ export const testApolloServer = async (params?: {
*/
authUserId?: string
}) => {
const initialCtx: GraphQLContext | undefined = params?.authUserId
? createTestContext({
auth: true,
userId: params.authUserId,
role: Roles.Server.User,
token: 'asd',
scopes: AllScopes
})
: params?.context
let baseCtx: GraphQLContext
if (params?.authUserId) {
baseCtx = createAuthedTestContext(params.authUserId)
} else if (params?.context) {
baseCtx = params.context
} else {
baseCtx = createTestContext()
}
const instance = initialCtx
? await buildApolloServer({
context: initialCtx
})
: await buildUnauthenticatedApolloServer()
const instance = await buildApolloServer()
/**
* Execute an operation against Apollo and get a properly typed response
@@ -96,26 +145,31 @@ export const testApolloServer = async (params?: {
*/
assertNoErrors: boolean
}>
): Promise<TypedGraphqlResponse<R>> => {
const realInstance = options?.context
? await buildApolloServer({
context: createTestContext({
...(initialCtx || {}),
...options.context
})
): Promise<ExecuteOperationResponse<R>> => {
const ctx = options?.context
? createTestContext({
...(baseCtx || {}),
...options.context
})
: instance
: baseCtx
const res = (await realInstance.executeOperation({
query,
variables
})) as TypedGraphqlResponse<R>
const res = (await instance.executeOperation(
{
query,
variables
},
{ contextValue: ctx }
)) as TypedGraphqlResponse<R>
if (options?.assertNoErrors) {
expect(res).to.not.haveGraphQLErrors()
}
return res
const results = getResponseResults(res)
return {
...results,
res
}
}
return { execute, server: instance }
+3 -2
View File
@@ -1,5 +1,5 @@
import { Optional } from '@/modules/shared/helpers/typeHelper'
import { GraphQLResponse } from 'apollo-server-core'
import { ExecuteOperationResponse } from '@/test/graphqlHelper'
import { AssertionError } from 'chai'
import { isString } from 'lodash'
@@ -21,7 +21,8 @@ const graphqlChaiPlugin: Chai.ChaiPlugin = (_chai, utils) => {
Assertion.prototype,
'haveGraphQLErrors',
function (
this: ChaiPluginThis<GraphQLResponse>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this: ChaiPluginThis<ExecuteOperationResponse<any>>,
messageOrOptions?: string | ({ message: string } | { code: string })
) {
const message = isString(messageOrOptions)
-35
View File
@@ -1,40 +1,5 @@
import { buildApolloServer } from '@/app'
import { Roles, AllScopes } from '@/modules/core/helpers/mainConstants'
import { addLoadersToCtx } from '@/modules/shared/middleware'
import net from 'net'
/**
* Build an ApolloServer instance with an authenticated context
*/
export function buildAuthenticatedApolloServer(
userId: string,
role = Roles.Server.User,
scopes = AllScopes
) {
return buildApolloServer({
context: () =>
addLoadersToCtx({
auth: true,
userId,
role,
token: 'asd',
scopes
})
})
}
/**
* Build an unauthenticated ApolloServer instance
*/
export function buildUnauthenticatedApolloServer() {
return buildApolloServer({
context: () =>
addLoadersToCtx({
auth: false
})
})
}
export async function getFreeServerPort() {
return new Promise((res) => {
const srv = net.createServer()
+287 -402
View File
@@ -85,6 +85,15 @@ __metadata:
languageName: node
linkType: hard
"@apollo/cache-control-types@npm:^1.0.3":
version: 1.0.3
resolution: "@apollo/cache-control-types@npm:1.0.3"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/a588e52bfa51e37a1dcd667469c827cbd1145df131650478fa7c7e6f9b01eb71ce9147f94be60c5b3ee0d4f83fd3304da1a78a342ae254c12cfc18e6e3f1e615
languageName: node
linkType: hard
"@apollo/client@npm:^3.6.6, @apollo/client@npm:^3.7.0, @apollo/client@npm:^3.7.14, @apollo/client@npm:^3.8.0, @apollo/client@npm:^3.9.6":
version: 3.9.7
resolution: "@apollo/client@npm:3.9.7"
@@ -122,9 +131,9 @@ __metadata:
languageName: node
linkType: hard
"@apollo/protobufjs@npm:1.2.4":
version: 1.2.4
resolution: "@apollo/protobufjs@npm:1.2.4"
"@apollo/protobufjs@npm:1.2.7":
version: 1.2.7
resolution: "@apollo/protobufjs@npm:1.2.7"
dependencies:
"@protobufjs/aspromise": "npm:^1.1.2"
"@protobufjs/base64": "npm:^1.1.2"
@@ -137,36 +146,11 @@ __metadata:
"@protobufjs/pool": "npm:^1.1.0"
"@protobufjs/utf8": "npm:^1.1.0"
"@types/long": "npm:^4.0.0"
"@types/node": "npm:^10.1.0"
long: "npm:^4.0.0"
bin:
apollo-pbjs: bin/pbjs
apollo-pbts: bin/pbts
checksum: 10/9cd57a469f5dd4c7901060019cec2ff31e1c3e5bec9eb2f0eaa22af166a7aae605c7976204810818614fb1f7fba36dd0e3b0197b8187af7f427aacd4c30b00f5
languageName: node
linkType: hard
"@apollo/protobufjs@npm:1.2.6":
version: 1.2.6
resolution: "@apollo/protobufjs@npm:1.2.6"
dependencies:
"@protobufjs/aspromise": "npm:^1.1.2"
"@protobufjs/base64": "npm:^1.1.2"
"@protobufjs/codegen": "npm:^2.0.4"
"@protobufjs/eventemitter": "npm:^1.1.0"
"@protobufjs/fetch": "npm:^1.1.0"
"@protobufjs/float": "npm:^1.0.2"
"@protobufjs/inquire": "npm:^1.1.0"
"@protobufjs/path": "npm:^1.1.2"
"@protobufjs/pool": "npm:^1.1.0"
"@protobufjs/utf8": "npm:^1.1.0"
"@types/long": "npm:^4.0.0"
"@types/node": "npm:^10.1.0"
long: "npm:^4.0.0"
bin:
apollo-pbjs: bin/pbjs
apollo-pbts: bin/pbts
checksum: 10/a134200f943983edbb46cdf358a5e4364599870dc9be4dbce043dfa842903cb0ad4f16e1d99f06f49cf00fbfb6ad95dbac101c9187ce723c968eeb27e6c66822
checksum: 10/9b2c2d5daa5221397bc9cf37f3fa8a45dc6f217783d0fe51eca12895f88f8a5d1b66efba2e288657a1c2da5b2e20fe0eb649a440ceeb30bfc5a3af75ccea832d
languageName: node
linkType: hard
@@ -185,101 +169,171 @@ __metadata:
languageName: node
linkType: hard
"@apollo/utils.dropunuseddefinitions@npm:^1.1.0":
version: 1.1.0
resolution: "@apollo/utils.dropunuseddefinitions@npm:1.1.0"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/b66e07086ea65bcb94d84cfd5e6d90d0406c4e7f602c9a5e793c2001273380a4f61c287f60ee1d81d47d49d3a62ef3f0afb8049243540d3021ff445869124094
languageName: node
linkType: hard
"@apollo/utils.keyvaluecache@npm:^1.0.1":
version: 1.0.1
resolution: "@apollo/utils.keyvaluecache@npm:1.0.1"
"@apollo/server-gateway-interface@npm:^1.1.1":
version: 1.1.1
resolution: "@apollo/server-gateway-interface@npm:1.1.1"
dependencies:
"@apollo/utils.logger": "npm:^1.0.0"
"@apollo/usage-reporting-protobuf": "npm:^4.1.1"
"@apollo/utils.fetcher": "npm:^2.0.0"
"@apollo/utils.keyvaluecache": "npm:^2.1.0"
"@apollo/utils.logger": "npm:^2.0.0"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/af0e95399297aa403c32ffff08c6dfa91a70aae73d5954f36e357f045cdb7e89f3bb4c3e70816d244f8f18af21d257bc79e934dd8bbaa1214c5f6d42a6a825d0
languageName: node
linkType: hard
"@apollo/server@npm:^4.11.0":
version: 4.11.0
resolution: "@apollo/server@npm:4.11.0"
dependencies:
"@apollo/cache-control-types": "npm:^1.0.3"
"@apollo/server-gateway-interface": "npm:^1.1.1"
"@apollo/usage-reporting-protobuf": "npm:^4.1.1"
"@apollo/utils.createhash": "npm:^2.0.0"
"@apollo/utils.fetcher": "npm:^2.0.0"
"@apollo/utils.isnodelike": "npm:^2.0.0"
"@apollo/utils.keyvaluecache": "npm:^2.1.0"
"@apollo/utils.logger": "npm:^2.0.0"
"@apollo/utils.usagereporting": "npm:^2.1.0"
"@apollo/utils.withrequired": "npm:^2.0.0"
"@graphql-tools/schema": "npm:^9.0.0"
"@types/express": "npm:^4.17.13"
"@types/express-serve-static-core": "npm:^4.17.30"
"@types/node-fetch": "npm:^2.6.1"
async-retry: "npm:^1.2.1"
cors: "npm:^2.8.5"
express: "npm:^4.17.1"
loglevel: "npm:^1.6.8"
lru-cache: "npm:^7.10.1"
checksum: 10/3a5ac26b3db86076cfa9154e535563993da5d541449a2403dfb5e2fca6fab03af3ad71ed375fe051b301b856d1bb2089f94e8aede4ff34d05828a38830c6c593
negotiator: "npm:^0.6.3"
node-abort-controller: "npm:^3.1.1"
node-fetch: "npm:^2.6.7"
uuid: "npm:^9.0.0"
whatwg-mimetype: "npm:^3.0.0"
peerDependencies:
graphql: ^16.6.0
checksum: 10/2f4d20dfcab2261d7c090d81bebccfd56ead1b9740e964fb2b7bd65058bff57b9bc175c4a3b2eb0c7d5fc8a7cc4ea2685ac6d0d2a147244964fa4c432db7c30f
languageName: node
linkType: hard
"@apollo/utils.logger@npm:^1.0.0":
version: 1.0.0
resolution: "@apollo/utils.logger@npm:1.0.0"
checksum: 10/9be2b269d6d1cf2235c7b49a5edbb36c87589facf79516521df66d0c782709b7301a1365693b6e15d77f482d231bbb0fea4c2ae42faac7068cc4e014ce338c68
"@apollo/usage-reporting-protobuf@npm:^4.1.0, @apollo/usage-reporting-protobuf@npm:^4.1.1":
version: 4.1.1
resolution: "@apollo/usage-reporting-protobuf@npm:4.1.1"
dependencies:
"@apollo/protobufjs": "npm:1.2.7"
checksum: 10/07679e0058d0f67200bcbb05405697d4052dd6d921b8ed717878d75c60efe5af4dd1c387f9e72be17d050967b3c334ee3eab8954c4dc40aed0f1013eb30fb251
languageName: node
linkType: hard
"@apollo/utils.printwithreducedwhitespace@npm:^1.1.0":
version: 1.1.0
resolution: "@apollo/utils.printwithreducedwhitespace@npm:1.1.0"
"@apollo/utils.createhash@npm:^2.0.0":
version: 2.0.1
resolution: "@apollo/utils.createhash@npm:2.0.1"
dependencies:
"@apollo/utils.isnodelike": "npm:^2.0.1"
sha.js: "npm:^2.4.11"
checksum: 10/9e3ba58fd44f7900133a2219b0b66c0656a9c729f7a2ed1a459af8f4149925f0602d9766e57a0cc2acb8d24623f5c34ebad0faac0004cd59060fd6b1c91d5029
languageName: node
linkType: hard
"@apollo/utils.dropunuseddefinitions@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.dropunuseddefinitions@npm:2.0.1"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/86536751681c64f35a2d37b0c2f69a39d91ea0e4f0c3c993d9f76fa109f85e9d306e6994bd6e38eef1e4e5b83245125aaa125ecc94e185d90b3255f06a538503
checksum: 10/c12166f2551fb44045a8210317b7776abc263136bd07bfe3c6eecdb050468590fc73e524efc437cad21cc4cfcd1efc3e110285025150c2073a4b303934898ac1
languageName: node
linkType: hard
"@apollo/utils.removealiases@npm:1.0.0":
version: 1.0.0
resolution: "@apollo/utils.removealiases@npm:1.0.0"
"@apollo/utils.fetcher@npm:^2.0.0":
version: 2.0.1
resolution: "@apollo/utils.fetcher@npm:2.0.1"
checksum: 10/e173d215c3544dade7b4a08733234d5180973c79e8e738e9e2530f2067e8731a5faa7f15176f4ca91f3cc95a4c70166a686c7382a6c6100f56ad5befcd613f9f
languageName: node
linkType: hard
"@apollo/utils.isnodelike@npm:^2.0.0, @apollo/utils.isnodelike@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.isnodelike@npm:2.0.1"
checksum: 10/c2e858186a60cccb7e4fc53e8b97b2a4d5470cd4975ad9cccd29e57a23eff1aa3a0c03edceb13c423632224ce2c327c6f1bb8bd77dc3fb039316bba5750536ec
languageName: node
linkType: hard
"@apollo/utils.keyvaluecache@npm:^2.1.0":
version: 2.1.1
resolution: "@apollo/utils.keyvaluecache@npm:2.1.1"
dependencies:
"@apollo/utils.logger": "npm:^2.0.1"
lru-cache: "npm:^7.14.1"
checksum: 10/9a6bc7c4645415329a93e77861cb1a9874b2171b741a3a667c277c6339f2ba46fb40011982e7b0993b118af1cc02e59e58fcbe7033ca6216cefec01e7b8eeda6
languageName: node
linkType: hard
"@apollo/utils.logger@npm:^2.0.0, @apollo/utils.logger@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.logger@npm:2.0.1"
checksum: 10/f975c81fcc7e54669b975031349f292930dc4cc3dd6bdc58bc7fe2159e0398a7d18b28860ee324c23722b005848e258094a143d20f6989fde5837379240b0066
languageName: node
linkType: hard
"@apollo/utils.printwithreducedwhitespace@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.printwithreducedwhitespace@npm:2.0.1"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/fda30ad4ee1fbf012e4289b9963b8b75a102eadbdfa5e558dc923cfc68df42eff6e232dc20c34b7e7563e5aac7ae3781d17919cd8f5eccb90c4225a274b2af93
checksum: 10/16cd191e66f3801b15deb581426cd1f55066bb824c32d63fe9de9c255bea2e2b6ee1ffc88873607830d2df0f3b4d9a14c707b709f205062e21a502f08f40d513
languageName: node
linkType: hard
"@apollo/utils.sortast@npm:^1.1.0":
version: 1.1.0
resolution: "@apollo/utils.sortast@npm:1.1.0"
"@apollo/utils.removealiases@npm:2.0.1":
version: 2.0.1
resolution: "@apollo/utils.removealiases@npm:2.0.1"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/2f3f925b239bce49fe9d80bb9fbb551992c8d9180af160e780faf1c88971a30ef16b842e82e1f27a0e1f8c649af0a442ef95f6838d4cde6148939ec73d9464f6
languageName: node
linkType: hard
"@apollo/utils.sortast@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.sortast@npm:2.0.1"
dependencies:
lodash.sortby: "npm:^4.7.0"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/5ec695d8c91efd82ad75cb3e27662644c71e22be71793908135b38965be6fe1f229c24fd2f4fed1bc1829b84bec2a1f6470817a83c633d95292db7635a625471
checksum: 10/b71245558ebd64bf93b98aec933d4b5f5758e0fecf7915728d94725ed4201fb2515e2af92fe01a595638147e5e0ef50a27ab5323d9b76eeb126769fb1e58f051
languageName: node
linkType: hard
"@apollo/utils.stripsensitiveliterals@npm:^1.2.0":
version: 1.2.0
resolution: "@apollo/utils.stripsensitiveliterals@npm:1.2.0"
"@apollo/utils.stripsensitiveliterals@npm:^2.0.1":
version: 2.0.1
resolution: "@apollo/utils.stripsensitiveliterals@npm:2.0.1"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/5910186a30be23fac59652d350e83a8a7a53adb9146ed545906f6893ad9c8d380752e679348ee210ae01fa39cc0487692b632e960003dcedc2282bd28de2aa01
checksum: 10/a3f74af0626f89d61f7ed1d25194f6b77006a06653399eecaea0b246cf685a85465091f2dc70280b127871b5c1eda7ded799ce176271c2612946acdc9453d388
languageName: node
linkType: hard
"@apollo/utils.usagereporting@npm:^1.0.0":
version: 1.0.0
resolution: "@apollo/utils.usagereporting@npm:1.0.0"
"@apollo/utils.usagereporting@npm:^2.1.0":
version: 2.1.0
resolution: "@apollo/utils.usagereporting@npm:2.1.0"
dependencies:
"@apollo/utils.dropunuseddefinitions": "npm:^1.1.0"
"@apollo/utils.printwithreducedwhitespace": "npm:^1.1.0"
"@apollo/utils.removealiases": "npm:1.0.0"
"@apollo/utils.sortast": "npm:^1.1.0"
"@apollo/utils.stripsensitiveliterals": "npm:^1.2.0"
apollo-reporting-protobuf: "npm:^3.3.1"
"@apollo/usage-reporting-protobuf": "npm:^4.1.0"
"@apollo/utils.dropunuseddefinitions": "npm:^2.0.1"
"@apollo/utils.printwithreducedwhitespace": "npm:^2.0.1"
"@apollo/utils.removealiases": "npm:2.0.1"
"@apollo/utils.sortast": "npm:^2.0.1"
"@apollo/utils.stripsensitiveliterals": "npm:^2.0.1"
peerDependencies:
graphql: 14.x || 15.x || 16.x
checksum: 10/e243fa4495e77bfbe5cfcf5bff1f3f7a26493eac1db9b98104263906c24f93dd64ed67fa4308f6868fef960d08d718c07508c15c6668ee8e78fa05565b438158
checksum: 10/8af4b23000a4c35ba568e6a532e4120ab0e55b291c7b902f2d10a51aad877d0438b80c019296436870ee265edcc8881521fb9a0829796f23a3b2cb73449ac890
languageName: node
linkType: hard
"@apollographql/apollo-tools@npm:^0.5.3":
version: 0.5.4
resolution: "@apollographql/apollo-tools@npm:0.5.4"
peerDependencies:
graphql: ^14.2.1 || ^15.0.0 || ^16.0.0
checksum: 10/4f69566d23ffb77ffedd87c679dcab608400f297e4cd5423151977b917737c427015485a8e0436feeb5154574171742ab626fb1a8f5ae2739070757976fd49f2
languageName: node
linkType: hard
"@apollographql/graphql-playground-html@npm:1.6.29":
version: 1.6.29
resolution: "@apollographql/graphql-playground-html@npm:1.6.29"
dependencies:
xss: "npm:^1.0.8"
checksum: 10/5e45cdc122dbc18c71f89fd9be8c19d1e35417ea27d3915206438d351f7775894957cd5b8bb378921bb96a8f6e6a9d182ce3d674abaddefd36a3a7e9cf6f1e68
"@apollo/utils.withrequired@npm:^2.0.0":
version: 2.0.1
resolution: "@apollo/utils.withrequired@npm:2.0.1"
checksum: 10/ddd3a72d0f13e6283128d1aae787b65f8ef0bf2f2cf351e143c479f0838679e72d82f42f653b6baadd33a092854fc9cb9dd8af4a45938ee25b718274cef408ee
languageName: node
linkType: hard
@@ -10500,27 +10554,15 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/merge@npm:8.3.1":
version: 8.3.1
resolution: "@graphql-tools/merge@npm:8.3.1"
"@graphql-tools/merge@npm:^8.4.1":
version: 8.4.2
resolution: "@graphql-tools/merge@npm:8.4.2"
dependencies:
"@graphql-tools/utils": "npm:8.9.0"
"@graphql-tools/utils": "npm:^9.2.1"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/9354a68aa1b851ee72d2d727a3a264279f1e5ed95100f6c6e7e0a2ad7449943d2ebe6fce43b4873a15e5c3e9df52ea9d23ff51ffc1f73c417c4ccf368f8486ab
languageName: node
linkType: hard
"@graphql-tools/merge@npm:8.3.6":
version: 8.3.6
resolution: "@graphql-tools/merge@npm:8.3.6"
dependencies:
"@graphql-tools/utils": "npm:8.12.0"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/511136c82cd40bcaa4570068859db7537b67fed0dd91918eb6447c10fed633beb790f39d846b3a3b982556a8f5f2aba860c920f2477023db92a1faac91ad36ac
checksum: 10/62a4e93812e11d083c17f7763f4333a29dbe99fddbff705ff5942a0bdbb9dcd14f668bd76bd3edda485534d5d1a7f09bac311b979196b6149df11d8968a83723
languageName: node
linkType: hard
@@ -10536,17 +10578,29 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/mock@npm:^8.1.2":
version: 8.7.6
resolution: "@graphql-tools/mock@npm:8.7.6"
"@graphql-tools/merge@npm:^9.0.6":
version: 9.0.7
resolution: "@graphql-tools/merge@npm:9.0.7"
dependencies:
"@graphql-tools/schema": "npm:9.0.4"
"@graphql-tools/utils": "npm:8.12.0"
"@graphql-tools/utils": "npm:^10.5.4"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/afe9b11f2d1e43dd3fce8e510aa20846137acfb6cb6d9362e5cfe954f2daaa5c169b77c3013a21f3263c690dee2f3b3e235e305e49c3cc398bdb166ddf2d082b
languageName: node
linkType: hard
"@graphql-tools/mock@npm:^9.0.4":
version: 9.0.4
resolution: "@graphql-tools/mock@npm:9.0.4"
dependencies:
"@graphql-tools/schema": "npm:^10.0.4"
"@graphql-tools/utils": "npm:^10.2.1"
fast-json-stable-stringify: "npm:^2.1.0"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/8acad4f9491993b7fd517f8491529eac0df24df257e42351d6fdaa9a6cf41936ec7015b1fc8c9219c6629fa60e9aca346578d98d75bf41005ce18063464bdfb3
checksum: 10/fca1716a70177da0361d68330676e5af37671b5dc16868202fe19f40cc4fc0df043d926923e7ad771ec2914ff360c83ef5624fc149637f3fbd10e82f2a7175f5
languageName: node
linkType: hard
@@ -10600,20 +10654,6 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/schema@npm:9.0.4":
version: 9.0.4
resolution: "@graphql-tools/schema@npm:9.0.4"
dependencies:
"@graphql-tools/merge": "npm:8.3.6"
"@graphql-tools/utils": "npm:8.12.0"
tslib: "npm:^2.4.0"
value-or-promise: "npm:1.0.11"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/8478d77b9d585e7f411563494a6eff4fd4fad5d507284b2384f57e3cf5b98f5ac875034b633afc902fe4accbcf5da1a629f460a4c27ca41bd3ce7dc16eb96bfe
languageName: node
linkType: hard
"@graphql-tools/schema@npm:^10.0.0, @graphql-tools/schema@npm:^10.0.3, @graphql-tools/schema@npm:^10.0.4":
version: 10.0.4
resolution: "@graphql-tools/schema@npm:10.0.4"
@@ -10628,17 +10668,31 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/schema@npm:^8.0.0":
version: 8.5.1
resolution: "@graphql-tools/schema@npm:8.5.1"
"@graphql-tools/schema@npm:^10.0.6":
version: 10.0.6
resolution: "@graphql-tools/schema@npm:10.0.6"
dependencies:
"@graphql-tools/merge": "npm:8.3.1"
"@graphql-tools/utils": "npm:8.9.0"
"@graphql-tools/merge": "npm:^9.0.6"
"@graphql-tools/utils": "npm:^10.5.4"
tslib: "npm:^2.4.0"
value-or-promise: "npm:1.0.11"
value-or-promise: "npm:^1.0.12"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/98f57502cc67ee48157bcf6f26c334e27b0673ec6f5a35c1a5bc1901772063c8bfdca435f81664ab1a41f9274b43dc78aa12791feee83546640d0a034b38c836
checksum: 10/f667ed6bf8c8419cee8929c19b1b31c55894526a25f36463d106baf31b19cee8b297281eef07de7e3d94173d03a8ade4570a6e338b1482a27f8eae09d9ade4a8
languageName: node
linkType: hard
"@graphql-tools/schema@npm:^9.0.0":
version: 9.0.19
resolution: "@graphql-tools/schema@npm:9.0.19"
dependencies:
"@graphql-tools/merge": "npm:^8.4.1"
"@graphql-tools/utils": "npm:^9.2.1"
tslib: "npm:^2.4.0"
value-or-promise: "npm:^1.0.12"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/762811fe08ec67000b190305783677ea086e6b300a1882f46b804bdf790e32de986bef7bbd574ddd4114393ca9b97422cc604390652537d4595eba7dde825259
languageName: node
linkType: hard
@@ -10665,28 +10719,6 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/utils@npm:8.12.0":
version: 8.12.0
resolution: "@graphql-tools/utils@npm:8.12.0"
dependencies:
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/9a3f377a7b8c069af4160ba76338a9ebfbe15c2c7290b3eb4bb11787a741abcda239e30af4e13be8cf899da38a64a5444ba993fa1dd2717165233dd66f4846d1
languageName: node
linkType: hard
"@graphql-tools/utils@npm:8.9.0":
version: 8.9.0
resolution: "@graphql-tools/utils@npm:8.9.0"
dependencies:
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/de5930b33664c53f0d22781bb16b4e029afaad165539faf80bd520adfad969c024891db672a2ff96195d8d1185bac66b284ebde67938e554d04c0798453da002
languageName: node
linkType: hard
"@graphql-tools/utils@npm:^10.0.0":
version: 10.0.11
resolution: "@graphql-tools/utils@npm:10.0.11"
@@ -10715,6 +10747,32 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/utils@npm:^10.5.4":
version: 10.5.4
resolution: "@graphql-tools/utils@npm:10.5.4"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.1.1"
cross-inspect: "npm:1.0.1"
dset: "npm:^3.1.2"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/e6750800be215cbd8b4cc67019ba91b881c6093a4ac71258eeef9f1f3101b1e81e862435fd5b05cb40d94d40e396d0a3629ad6613226d7a4f75b8c5329a03aa7
languageName: node
linkType: hard
"@graphql-tools/utils@npm:^9.2.1":
version: 9.2.1
resolution: "@graphql-tools/utils@npm:9.2.1"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.1.1"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/b1665043c2180a74d1e071f9f495ce16b2f46eeed1b319a290ae58f699629fe0a47b619c4f9be89135ff20b1c34fe6cc27e86570cf1e2cff07d3ae204f3d170d
languageName: node
linkType: hard
"@graphql-tools/wrap@npm:^10.0.2":
version: 10.0.5
resolution: "@graphql-tools/wrap@npm:10.0.5"
@@ -11171,13 +11229,6 @@ __metadata:
languageName: node
linkType: hard
"@josephg/resolvable@npm:^1.0.0":
version: 1.0.1
resolution: "@josephg/resolvable@npm:1.0.1"
checksum: 10/64eb763b5138bdae4fb59c0c0e89ed261b690917ae6bd777b533257668f151b8868698fb73dfd7665746ad07c7c917fe89ccfdf2404048d39f373f57f1a14e34
languageName: node
linkType: hard
"@jridgewell/gen-mapping@npm:^0.1.0":
version: 0.1.1
resolution: "@jridgewell/gen-mapping@npm:0.1.1"
@@ -15350,6 +15401,7 @@ __metadata:
dependencies:
"@apollo/client": "npm:^3.7.0"
"@apollo/rover": "npm:^0.23.0"
"@apollo/server": "npm:^4.11.0"
"@aws-sdk/client-s3": "npm:^3.276.0"
"@aws-sdk/lib-storage": "npm:^3.100.0"
"@bull-board/express": "npm:^4.2.2"
@@ -15360,7 +15412,8 @@ __metadata:
"@graphql-codegen/typescript": "npm:^4.0.7"
"@graphql-codegen/typescript-operations": "npm:^4.2.1"
"@graphql-codegen/typescript-resolvers": "npm:^4.1.0"
"@graphql-tools/schema": "npm:^10.0.4"
"@graphql-tools/mock": "npm:^9.0.4"
"@graphql-tools/schema": "npm:^10.0.6"
"@mailchimp/mailchimp_marketing": "npm:^3.0.80"
"@parcel/watcher": "npm:^2.4.1"
"@speckle/objectloader": "workspace:^"
@@ -15374,6 +15427,7 @@ __metadata:
"@types/compression": "npm:^1.7.2"
"@types/connect-redis": "npm:^0.0.23"
"@types/cookie-parser": "npm:^1.4.7"
"@types/cors": "npm:^2.8.17"
"@types/debug": "npm:^4.1.7"
"@types/deep-equal-in-any-order": "npm:^1.0.1"
"@types/ejs": "npm:^3.1.1"
@@ -15405,8 +15459,6 @@ __metadata:
"@typescript-eslint/eslint-plugin": "npm:^5.39.0"
"@typescript-eslint/parser": "npm:^5.39.0"
ajv: "npm:^8.12.0"
apollo-server-express: "npm:^3.10.2"
apollo-server-plugin-base: "npm:^3.7.2"
axios: "npm:^1.7.4"
bcrypt: "npm:^5.0.0"
bull: "npm:^4.8.5"
@@ -15435,10 +15487,11 @@ __metadata:
express-async-errors: "npm:^3.1.1"
express-prom-bundle: "npm:^6.6.0"
express-session: "npm:^1.17.1"
graphql: "npm:^15"
graphql: "npm:^16.6.0"
graphql-redis-subscriptions: "npm:^2.2.2"
graphql-scalars: "npm:^1.18.0"
graphql-subscriptions: "npm:^2.0.0"
graphql-tag: "npm:^2.12.6"
http-proxy-middleware: "npm:v3.0.0-beta.0"
ioredis: "npm:^5.2.2"
ioredis-mock: "npm:^8.9.0"
@@ -17719,15 +17772,6 @@ __metadata:
languageName: node
linkType: hard
"@types/accepts@npm:^1.3.5":
version: 1.3.5
resolution: "@types/accepts@npm:1.3.5"
dependencies:
"@types/node": "npm:*"
checksum: 10/3984edd631d9e308ef10286454a05e2388812a740d404abf93522a3bc3d10032ae6a60816e8cc4ae1bc96367db39e543d3ef862944cea53d1eea48be1f624fc2
languageName: node
linkType: hard
"@types/apollo-upload-client@npm:^17.0.1":
version: 17.0.1
resolution: "@types/apollo-upload-client@npm:17.0.1"
@@ -17834,7 +17878,7 @@ __metadata:
languageName: node
linkType: hard
"@types/body-parser@npm:*, @types/body-parser@npm:1.19.2":
"@types/body-parser@npm:*":
version: 1.19.2
resolution: "@types/body-parser@npm:1.19.2"
dependencies:
@@ -17971,10 +18015,12 @@ __metadata:
languageName: node
linkType: hard
"@types/cors@npm:2.8.12":
version: 2.8.12
resolution: "@types/cors@npm:2.8.12"
checksum: 10/8c45f112c7d1d2d831b4b266f2e6ed33a1887a35dcbfe2a18b28370751fababb7cd045e745ef84a523c33a25932678097bf79afaa367c6cb3fa0daa7a6438257
"@types/cors@npm:^2.8.17":
version: 2.8.17
resolution: "@types/cors@npm:2.8.17"
dependencies:
"@types/node": "npm:*"
checksum: 10/469bd85e29a35977099a3745c78e489916011169a664e97c4c3d6538143b0a16e4cc72b05b407dc008df3892ed7bf595f9b7c0f1f4680e169565ee9d64966bde
languageName: node
linkType: hard
@@ -18141,14 +18187,15 @@ __metadata:
languageName: node
linkType: hard
"@types/express-serve-static-core@npm:4.17.30":
version: 4.17.30
resolution: "@types/express-serve-static-core@npm:4.17.30"
"@types/express-serve-static-core@npm:^4.17.30":
version: 4.19.5
resolution: "@types/express-serve-static-core@npm:4.19.5"
dependencies:
"@types/node": "npm:*"
"@types/qs": "npm:*"
"@types/range-parser": "npm:*"
checksum: 10/1074c5769ae052fcb7ea0a2d4a807090a832ec6f8e1ca9105cd949f6dbc99a745a0d3e5b8c8fb64e7105b4a23d8283aa50fd3234d3b7f821899efbed3675b179
"@types/send": "npm:*"
checksum: 10/49350c6315eeb7d640e13e6138ba6005121b3b610b1e25746fccd5b86b559be810a4ba384b9bd7eee288975b5bd8cf67c1772c646254b812beaa488774eb5513
languageName: node
linkType: hard
@@ -18161,7 +18208,7 @@ __metadata:
languageName: node
linkType: hard
"@types/express@npm:*, @types/express@npm:4.17.13, @types/express@npm:^4.17.13":
"@types/express@npm:*, @types/express@npm:^4.17.13":
version: 4.17.13
resolution: "@types/express@npm:4.17.13"
dependencies:
@@ -18583,6 +18630,16 @@ __metadata:
languageName: node
linkType: hard
"@types/node-fetch@npm:^2.6.1":
version: 2.6.11
resolution: "@types/node-fetch@npm:2.6.11"
dependencies:
"@types/node": "npm:*"
form-data: "npm:^4.0.0"
checksum: 10/c416df8f182ec3826278ea42557fda08f169a48a05e60722d9c8edd4e5b2076ae281c6b6601ad406035b7201f885b0257983b61c26b3f9eb0f41192a807b5de5
languageName: node
linkType: hard
"@types/node-forge@npm:^1.3.0":
version: 1.3.11
resolution: "@types/node-forge@npm:1.3.11"
@@ -18608,13 +18665,6 @@ __metadata:
languageName: node
linkType: hard
"@types/node@npm:^10.1.0":
version: 10.17.60
resolution: "@types/node@npm:10.17.60"
checksum: 10/f9161493b3284b1d41d5d594c2768625acdd9e33f992f71ccde47861916e662e2ae438d2cc5f1b285053391a31b52a7564ecedc22d485610d236bfad9c7e6a1c
languageName: node
linkType: hard
"@types/node@npm:^13.7.0":
version: 13.13.52
resolution: "@types/node@npm:13.13.52"
@@ -19016,6 +19066,16 @@ __metadata:
languageName: node
linkType: hard
"@types/send@npm:*":
version: 0.17.4
resolution: "@types/send@npm:0.17.4"
dependencies:
"@types/mime": "npm:^1"
"@types/node": "npm:*"
checksum: 10/28320a2aa1eb704f7d96a65272a07c0bf3ae7ed5509c2c96ea5e33238980f71deeed51d3631927a77d5250e4091b3e66bce53b42d770873282c6a20bb8b0280d
languageName: node
linkType: hard
"@types/serve-index@npm:^1.9.1":
version: 1.9.1
resolution: "@types/serve-index@npm:1.9.1"
@@ -22638,146 +22698,6 @@ __metadata:
languageName: node
linkType: hard
"apollo-datasource@npm:^3.3.2":
version: 3.3.2
resolution: "apollo-datasource@npm:3.3.2"
dependencies:
"@apollo/utils.keyvaluecache": "npm:^1.0.1"
apollo-server-env: "npm:^4.2.1"
checksum: 10/70244e792655b357213b92e9dd0e8ca553724857847c9bedb53a1dbf7a92fc0d8b05a60d77203d6c30331599b44c5d7cc5f4d94c934465fa05b146b681ed2293
languageName: node
linkType: hard
"apollo-reporting-protobuf@npm:^3.3.1, apollo-reporting-protobuf@npm:^3.3.2":
version: 3.3.2
resolution: "apollo-reporting-protobuf@npm:3.3.2"
dependencies:
"@apollo/protobufjs": "npm:1.2.4"
checksum: 10/92b527b062a7ca65c6a19a01899a5d45e9271301fb5c5841e3e90e420d125cce5bf0e1575bc4bb1064720793a1ff270acb0387cf82d5decb3d6a08ecc767eb65
languageName: node
linkType: hard
"apollo-reporting-protobuf@npm:^3.4.0":
version: 3.4.0
resolution: "apollo-reporting-protobuf@npm:3.4.0"
dependencies:
"@apollo/protobufjs": "npm:1.2.6"
checksum: 10/d6c731c1e07f952770166c71222a34ea97dd90f4b1d74f3261caa1542e1fb81a591c74586cd973c28c12e8bb9aa54ff0de411698f2311978f7144f98258c1a0b
languageName: node
linkType: hard
"apollo-server-core@npm:^3.10.2":
version: 3.12.1
resolution: "apollo-server-core@npm:3.12.1"
dependencies:
"@apollo/utils.keyvaluecache": "npm:^1.0.1"
"@apollo/utils.logger": "npm:^1.0.0"
"@apollo/utils.usagereporting": "npm:^1.0.0"
"@apollographql/apollo-tools": "npm:^0.5.3"
"@apollographql/graphql-playground-html": "npm:1.6.29"
"@graphql-tools/mock": "npm:^8.1.2"
"@graphql-tools/schema": "npm:^8.0.0"
"@josephg/resolvable": "npm:^1.0.0"
apollo-datasource: "npm:^3.3.2"
apollo-reporting-protobuf: "npm:^3.4.0"
apollo-server-env: "npm:^4.2.1"
apollo-server-errors: "npm:^3.3.1"
apollo-server-plugin-base: "npm:^3.7.2"
apollo-server-types: "npm:^3.8.0"
async-retry: "npm:^1.2.1"
fast-json-stable-stringify: "npm:^2.1.0"
graphql-tag: "npm:^2.11.0"
loglevel: "npm:^1.6.8"
lru-cache: "npm:^6.0.0"
node-abort-controller: "npm:^3.0.1"
sha.js: "npm:^2.4.11"
uuid: "npm:^9.0.0"
whatwg-mimetype: "npm:^3.0.0"
peerDependencies:
graphql: ^15.3.0 || ^16.0.0
checksum: 10/2e273d60a91ef7faf85687445bcb5f1affcfcd85a363968d570c82a57ce533076780be14e8d55c4d3ed7168fa19167960830bce5525436cc78a5e5e71752c473
languageName: node
linkType: hard
"apollo-server-env@npm:^4.2.1":
version: 4.2.1
resolution: "apollo-server-env@npm:4.2.1"
dependencies:
node-fetch: "npm:^2.6.7"
checksum: 10/9172288c89c2ebb2a02d58542f807896de1ca0ba80c430f09242f2fa9ece40d7ecb8f9527357ba5e1d9997c64c364e7a9716e4f5485c5fb4938f69627bf1cea4
languageName: node
linkType: hard
"apollo-server-errors@npm:^3.3.1":
version: 3.3.1
resolution: "apollo-server-errors@npm:3.3.1"
peerDependencies:
graphql: ^15.3.0 || ^16.0.0
checksum: 10/5090af0280e40ce9b3b042fca6bd195d84c8507d8fe0b16b7fd52501b8f88cb2b5cca99882a9c5253f04c78b94ff2d350eb148867e20b0ddd6211071bb17f553
languageName: node
linkType: hard
"apollo-server-express@npm:^3.10.2":
version: 3.10.2
resolution: "apollo-server-express@npm:3.10.2"
dependencies:
"@types/accepts": "npm:^1.3.5"
"@types/body-parser": "npm:1.19.2"
"@types/cors": "npm:2.8.12"
"@types/express": "npm:4.17.13"
"@types/express-serve-static-core": "npm:4.17.30"
accepts: "npm:^1.3.5"
apollo-server-core: "npm:^3.10.2"
apollo-server-types: "npm:^3.6.2"
body-parser: "npm:^1.19.0"
cors: "npm:^2.8.5"
parseurl: "npm:^1.3.3"
peerDependencies:
express: ^4.17.1
graphql: ^15.3.0 || ^16.0.0
checksum: 10/a26a8fd794824c49ca4c866347fea3f3553a59e58a14dc1ed2af737173eeda7a62ce69b99aaaaf2d7e3ba20eeb222583c1e45db88f6539e80978d2a3e1e337b9
languageName: node
linkType: hard
"apollo-server-plugin-base@npm:^3.7.2":
version: 3.7.2
resolution: "apollo-server-plugin-base@npm:3.7.2"
dependencies:
apollo-server-types: "npm:^3.8.0"
peerDependencies:
graphql: ^15.3.0 || ^16.0.0
checksum: 10/b2599f51e66dce930208c1c6f6b4394e3bde6c635e971a80d677b33e7d3d6c2050453ede99bde66281e4d6d6675094b6fb50a5ec30d16e04bee13d7570ad2715
languageName: node
linkType: hard
"apollo-server-types@npm:^3.6.2":
version: 3.6.2
resolution: "apollo-server-types@npm:3.6.2"
dependencies:
"@apollo/utils.keyvaluecache": "npm:^1.0.1"
"@apollo/utils.logger": "npm:^1.0.0"
apollo-reporting-protobuf: "npm:^3.3.2"
apollo-server-env: "npm:^4.2.1"
peerDependencies:
graphql: ^15.3.0 || ^16.0.0
checksum: 10/c513b2890ee453beb859041540a6b35cb6db6c8a7060f30a572b7531d6b998b8fe99519966a9052b4fd7c962d6bd1a62ab61036b933db8eaa1981a54f92e2785
languageName: node
linkType: hard
"apollo-server-types@npm:^3.8.0":
version: 3.8.0
resolution: "apollo-server-types@npm:3.8.0"
dependencies:
"@apollo/utils.keyvaluecache": "npm:^1.0.1"
"@apollo/utils.logger": "npm:^1.0.0"
apollo-reporting-protobuf: "npm:^3.4.0"
apollo-server-env: "npm:^4.2.1"
peerDependencies:
graphql: ^15.3.0 || ^16.0.0
checksum: 10/c802fecba27ff5f0b45fc4a3d6c88e18c39c6e5ba5785db067588d4e0c7d56aba6f4dc69171b07ac6348e9e313b036c57c178af58b8b3414331517e0b280324e
languageName: node
linkType: hard
"apollo-upload-client@npm:^17.0.0":
version: 17.0.0
resolution: "apollo-upload-client@npm:17.0.0"
@@ -23978,26 +23898,6 @@ __metadata:
languageName: node
linkType: hard
"body-parser@npm:^1.19.0":
version: 1.20.0
resolution: "body-parser@npm:1.20.0"
dependencies:
bytes: "npm:3.1.2"
content-type: "npm:~1.0.4"
debug: "npm:2.6.9"
depd: "npm:2.0.0"
destroy: "npm:1.2.0"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
on-finished: "npm:2.4.1"
qs: "npm:6.10.3"
raw-body: "npm:2.5.1"
type-is: "npm:~1.6.18"
unpipe: "npm:1.0.0"
checksum: 10/63fe82c27fdacac51d26665c3d13d4c6e48d1c3e9efe1fbc0fd18801aa9a598ab1023b09298ae4b3d0a7598d55902d793f7fa1b5551da99c16eabfed9b022a51
languageName: node
linkType: hard
"bonjour-service@npm:^1.0.11":
version: 1.0.12
resolution: "bonjour-service@npm:1.0.12"
@@ -25618,7 +25518,7 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^2.19.0, commander@npm:^2.20.0, commander@npm:^2.20.3":
"commander@npm:^2.19.0, commander@npm:^2.20.0":
version: 2.20.3
resolution: "commander@npm:2.20.3"
checksum: 10/90c5b6898610cd075984c58c4f88418a4fb44af08c1b1415e9854c03171bec31b336b7f3e4cefe33de994b3f12b03c5e2d638da4316df83593b9e82554e7e95b
@@ -26442,6 +26342,15 @@ __metadata:
languageName: node
linkType: hard
"cross-inspect@npm:1.0.1":
version: 1.0.1
resolution: "cross-inspect@npm:1.0.1"
dependencies:
tslib: "npm:^2.4.0"
checksum: 10/7c1e02e0a9670b62416a3ea1df7ae880fdad3aa0a857de8932c4e5f8acd71298c7e3db9da8e9da603f5692cd1879938f5e72e34a9f5d1345987bef656d117fc1
languageName: node
linkType: hard
"cross-spawn@npm:7.0.3, cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@@ -26646,13 +26555,6 @@ __metadata:
languageName: node
linkType: hard
"cssfilter@npm:0.0.10":
version: 0.0.10
resolution: "cssfilter@npm:0.0.10"
checksum: 10/1e45182f42de848f092f50a313113c28a88e4ac98333bf1603ee1c3b200384a3bc83c12e35cd61135e3b0f218295f600d51120ca1f926b7958b2d3262d711214
languageName: node
linkType: hard
"cssnano-preset-default@npm:^6.0.1":
version: 6.0.1
resolution: "cssnano-preset-default@npm:6.0.1"
@@ -29923,7 +29825,7 @@ __metadata:
languageName: node
linkType: hard
"express@npm:>=4.19.2, express@npm:^4.17.3, express@npm:^4.19.2":
"express@npm:>=4.19.2, express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.19.2":
version: 4.19.2
resolution: "express@npm:4.19.2"
dependencies:
@@ -31988,10 +31890,17 @@ __metadata:
languageName: node
linkType: hard
"graphql@npm:^15.3.0":
version: 15.8.0
resolution: "graphql@npm:15.8.0"
checksum: 10/f8d830287a9028d6779b59c437e0ade63a713b47521b02b60316df1761b805b1a7ce03be88053d224b7f78f5d1d1a786d287ab229cd158b42ebeea9e86daaba5
"graphql@npm:14 - 16, graphql@npm:^16.6.0":
version: 16.9.0
resolution: "graphql@npm:16.9.0"
checksum: 10/5833f82bb6c31bec120bbf9cd400eda873e1bb7ef5c17974fa262cd82dc68728fda5d4cb859dc8aaa4c4fe4f6fe1103a9c47efc01a12c02ae5cb581d8e4029e2
languageName: node
linkType: hard
"graphql@npm:^15.0.0":
version: 15.9.0
resolution: "graphql@npm:15.9.0"
checksum: 10/ce1f50672bcb369395d07a47048bcbb429ed1ce06dbcafb7a0999df791cb7aa7206be21497907973dbc8a01df3cd7f632f43c583f248538f186f5adfa1a0d1c5
languageName: node
linkType: hard
@@ -39064,10 +38973,10 @@ __metadata:
languageName: node
linkType: hard
"node-abort-controller@npm:^3.0.1":
version: 3.0.1
resolution: "node-abort-controller@npm:3.0.1"
checksum: 10/7437b015830a2f714692fd372c01ce5c8c66f332a205455f58ddc8b3228314e588a20abd34a2b037c9cc438ced74e75492c7fc04f4dc0cf7bf0c0ac4160175e3
"node-abort-controller@npm:^3.1.1":
version: 3.1.1
resolution: "node-abort-controller@npm:3.1.1"
checksum: 10/0a2cdb7ec0aeaf3cb31e1ca0e192f5add48f1c5c9c9ed822129f9dddbd9432f69b7425982f94ce803c56a2104884530aa67cd57696e5774b2e5b8ec2f58de042
languageName: node
linkType: hard
@@ -43622,15 +43531,6 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:6.10.3, qs@npm:^6.4.0, qs@npm:^6.5.1":
version: 6.10.3
resolution: "qs@npm:6.10.3"
dependencies:
side-channel: "npm:^1.0.4"
checksum: 10/73d07bfd77f07bec3750dca5e6d165cba0c87ce3e4688bb26e5e462e725ab1289ecdb69164b0b4a4d1b913e2a3ae6b22acbb8b2feb5c8f31bd76f2380f3dc23d
languageName: node
linkType: hard
"qs@npm:6.11.0, qs@npm:^6.10.0":
version: 6.11.0
resolution: "qs@npm:6.11.0"
@@ -43640,6 +43540,15 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:^6.4.0, qs@npm:^6.5.1":
version: 6.10.3
resolution: "qs@npm:6.10.3"
dependencies:
side-channel: "npm:^1.0.4"
checksum: 10/73d07bfd77f07bec3750dca5e6d165cba0c87ce3e4688bb26e5e462e725ab1289ecdb69164b0b4a4d1b913e2a3ae6b22acbb8b2feb5c8f31bd76f2380f3dc23d
languageName: node
linkType: hard
"qs@npm:~6.5.2":
version: 6.5.3
resolution: "qs@npm:6.5.3"
@@ -43757,18 +43666,6 @@ __metadata:
languageName: node
linkType: hard
"raw-body@npm:2.5.1":
version: 2.5.1
resolution: "raw-body@npm:2.5.1"
dependencies:
bytes: "npm:3.1.2"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
unpipe: "npm:1.0.0"
checksum: 10/280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84
languageName: node
linkType: hard
"raw-body@npm:2.5.2":
version: 2.5.2
resolution: "raw-body@npm:2.5.2"
@@ -50102,7 +49999,7 @@ __metadata:
languageName: node
linkType: hard
"value-or-promise@npm:1.0.11, value-or-promise@npm:^1.0.11":
"value-or-promise@npm:^1.0.11":
version: 1.0.11
resolution: "value-or-promise@npm:1.0.11"
checksum: 10/9bd1cf82be5b59ec4a7ee9fa17ca7b3f16165c3ea33ebabe514f7a20e4f88dd11f912900f0279760618eb7fbd5e3bb2a4cf4b351b5fd8e8da69aa2719725e54a
@@ -52130,18 +52027,6 @@ __metadata:
languageName: node
linkType: hard
"xss@npm:^1.0.8":
version: 1.0.11
resolution: "xss@npm:1.0.11"
dependencies:
commander: "npm:^2.20.3"
cssfilter: "npm:0.0.10"
bin:
xss: bin/xss
checksum: 10/00f0e85d7b7e5e1b2908366866637f8aa0707c7f433837c553e45f94bd824897e89b4332a092fcf2bfe8f66f196e0c6570a0069ed60c24f0e26a566bbb5b51e2
languageName: node
linkType: hard
"xtend@npm:^2.2.0":
version: 2.2.0
resolution: "xtend@npm:2.2.0"