Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Migrate to Apollo v5 (#119)

* server: Replace error classes no longer provided by Apollo

* server: Remove no longer available DataSource base class

* server: Remove no longer necessary gql tag

* server: Upgrade Apollo packages

* server: Refactor API server bootstrap

* server: Remove Apollo packages that reached end-of-life

* server/client: Bump graphql package version

* fixup! server: Refactor API server bootstrap

* fixup! server: Remove Apollo packages that reached end-of-life

* fixup! server: Refactor API server bootstrap

* fixup! server: Replace error classes no longer provided by Apollo

* fix merge main and final code review changes

* lint fixes

---------

Co-authored-by: Juan S. Mrad <juansmrad@gmail.com>

authored by

Paweł Wieczorek
Juan S. Mrad
and committed by
GitHub
ef1b5bfc 183d5baf

+925 -927
+5 -43
client/package-lock.json
··· 50 50 "clsx": "^2.1.1", 51 51 "date-fns": "^3.6.0", 52 52 "framer-motion": "^11.1.7", 53 - "graphql": "^16.2.0", 53 + "graphql": "^16.13.2", 54 54 "jsonpointer": "^5.0.1", 55 55 "latlon-geohash": "^2.0.0", 56 56 "lodash": "^4.18.1", ··· 2875 2875 "arm" 2876 2876 ], 2877 2877 "dev": true, 2878 - "libc": [ 2879 - "glibc" 2880 - ], 2881 2878 "license": "MIT", 2882 2879 "optional": true, 2883 2880 "os": [ ··· 2892 2889 "arm" 2893 2890 ], 2894 2891 "dev": true, 2895 - "libc": [ 2896 - "musl" 2897 - ], 2898 2892 "license": "MIT", 2899 2893 "optional": true, 2900 2894 "os": [ ··· 2909 2903 "arm64" 2910 2904 ], 2911 2905 "dev": true, 2912 - "libc": [ 2913 - "glibc" 2914 - ], 2915 2906 "license": "MIT", 2916 2907 "optional": true, 2917 2908 "os": [ ··· 2926 2917 "arm64" 2927 2918 ], 2928 2919 "dev": true, 2929 - "libc": [ 2930 - "musl" 2931 - ], 2932 2920 "license": "MIT", 2933 2921 "optional": true, 2934 2922 "os": [ ··· 2943 2931 "loong64" 2944 2932 ], 2945 2933 "dev": true, 2946 - "libc": [ 2947 - "glibc" 2948 - ], 2949 2934 "license": "MIT", 2950 2935 "optional": true, 2951 2936 "os": [ ··· 2960 2945 "loong64" 2961 2946 ], 2962 2947 "dev": true, 2963 - "libc": [ 2964 - "musl" 2965 - ], 2966 2948 "license": "MIT", 2967 2949 "optional": true, 2968 2950 "os": [ ··· 2977 2959 "ppc64" 2978 2960 ], 2979 2961 "dev": true, 2980 - "libc": [ 2981 - "glibc" 2982 - ], 2983 2962 "license": "MIT", 2984 2963 "optional": true, 2985 2964 "os": [ ··· 2994 2973 "ppc64" 2995 2974 ], 2996 2975 "dev": true, 2997 - "libc": [ 2998 - "musl" 2999 - ], 3000 2976 "license": "MIT", 3001 2977 "optional": true, 3002 2978 "os": [ ··· 3011 2987 "riscv64" 3012 2988 ], 3013 2989 "dev": true, 3014 - "libc": [ 3015 - "glibc" 3016 - ], 3017 2990 "license": "MIT", 3018 2991 "optional": true, 3019 2992 "os": [ ··· 3028 3001 "riscv64" 3029 3002 ], 3030 3003 "dev": true, 3031 - "libc": [ 3032 - "musl" 3033 - ], 3034 3004 "license": "MIT", 3035 3005 "optional": true, 3036 3006 "os": [ ··· 3045 3015 "s390x" 3046 3016 ], 3047 3017 "dev": true, 3048 - "libc": [ 3049 - "glibc" 3050 - ], 3051 3018 "license": "MIT", 3052 3019 "optional": true, 3053 3020 "os": [ ··· 3062 3029 "x64" 3063 3030 ], 3064 3031 "dev": true, 3065 - "libc": [ 3066 - "glibc" 3067 - ], 3068 3032 "license": "MIT", 3069 3033 "optional": true, 3070 3034 "os": [ ··· 3079 3043 "x64" 3080 3044 ], 3081 3045 "dev": true, 3082 - "libc": [ 3083 - "musl" 3084 - ], 3085 3046 "license": "MIT", 3086 3047 "optional": true, 3087 3048 "os": [ ··· 7665 7626 "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 7666 7627 }, 7667 7628 "node_modules/graphql": { 7668 - "version": "16.8.2", 7669 - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.2.tgz", 7670 - "integrity": "sha512-cvVIBILwuoSyD54U4cF/UXDh5yAobhNV/tPygI4lZhgOIJQE/WLWC4waBRb4I6bDVYb3OVx3lfHbaQOEoUD5sg==", 7629 + "version": "16.13.2", 7630 + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", 7631 + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", 7632 + "license": "MIT", 7671 7633 "engines": { 7672 7634 "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" 7673 7635 }
+1 -1
client/package.json
··· 57 57 "clsx": "^2.1.1", 58 58 "date-fns": "^3.6.0", 59 59 "framer-motion": "^11.1.7", 60 - "graphql": "^16.2.0", 60 + "graphql": "^16.13.2", 61 61 "jsonpointer": "^5.0.1", 62 62 "latlon-geohash": "^2.0.0", 63 63 "lodash": "^4.18.1",
+117 -120
server/api.ts
··· 1 1 /* eslint-disable max-lines */ 2 - // In this case, we want to rely on apollo-server-express bundling a 3 - // corresponding version of apollo-server-core, rather than picking an 4 - // apollo-server-core version in package.json 5 - // eslint-disable-next-line import/no-extraneous-dependencies 6 2 import os from 'node:os'; 7 3 import path from 'path'; 4 + import { ApolloServer } from '@apollo/server'; 5 + import { unwrapResolverError } from '@apollo/server/errors'; 6 + import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled'; 7 + import { expressMiddleware } from '@as-integrations/express4'; 8 8 import { makeExecutableSchema } from '@graphql-tools/schema'; 9 9 import { MapperKind, mapSchema } from '@graphql-tools/utils'; 10 10 import { SpanStatusCode } from '@opentelemetry/api'; ··· 13 13 SEMATTRS_EXCEPTION_STACKTRACE, 14 14 SEMATTRS_EXCEPTION_TYPE, 15 15 } from '@opentelemetry/semantic-conventions'; 16 - import { 17 - ApolloError, 18 - ApolloServerPluginLandingPageDisabled, 19 - ApolloServerPluginLandingPageGraphQLPlayground, 20 - } from 'apollo-server-core'; 21 - import { ApolloServer } from 'apollo-server-express'; 16 + import { GraphQLError, type GraphQLFormattedError } from 'graphql'; 22 17 import connectPgSimple from 'connect-pg-simple'; 23 18 import cors from 'cors'; 24 19 import express, { type ErrorRequestHandler } from 'express'; ··· 34 29 makeLoginSsoRequiredError, 35 30 makeLoginUserDoesNotExistError, 36 31 } from './graphql/datasources/UserApi.js'; 37 - import resolvers from './graphql/resolvers.js'; 32 + import resolvers, { type Context } from './graphql/resolvers.js'; 38 33 import typeDefs from './graphql/schema.js'; 39 34 import { authSchemaWrapper } from './graphql/utils/authorization.js'; 40 35 import { type Dependencies } from './iocContainer/index.js'; ··· 342 337 /** 343 338 * Apollo Server - uses /api/graphql path 344 339 */ 345 - const apolloServer = new ApolloServer({ 340 + const apolloServer = new ApolloServer<Context>({ 346 341 schema: mapSchema(makeExecutableSchema({ typeDefs, resolvers }), { 347 342 [MapperKind.QUERY_ROOT_FIELD]( 348 343 fieldConfig, ··· 361 356 return authSchemaWrapper(fieldConfig, schema); 362 357 }, 363 358 }), 364 - dataSources: () => deps.DataSources, 365 - context: ({ req, res }) => { 366 - return { 367 - ...buildContext({ req, res }), 368 - services: makeGqlServices(deps), 369 - }; 370 - }, 371 359 plugins: [ 372 - { 373 - ...(process.env.NODE_ENV === 'production' 374 - ? ApolloServerPluginLandingPageDisabled() 375 - : ApolloServerPluginLandingPageGraphQLPlayground()), 376 - }, 360 + ...(process.env.NODE_ENV === 'production' 361 + ? [ApolloServerPluginLandingPageDisabled()] 362 + : []), 377 363 ], 378 364 validationRules: [depthLimit(safeGetEnvInt('GRAPHQL_MAX_DEPTH', 10))], 379 365 introspection: process.env.NODE_ENV !== 'production', 380 - formatError(e) { 381 - // `e` can be an ApolloError instance, but will only be one if such an 382 - // instance (or an ApolloError subclass) was explicitly thrown from a 383 - // resolver. In that case, we assume the thrower knows they're dealing 384 - // with apollo, and we can just pass the error through as-is. 385 - if (e instanceof ApolloError) { 386 - return e; 366 + formatError(formattedError, error) { 367 + // unwrapResolverError removes the GraphQLError wrapper added by graphql-js 368 + // when a non-GraphQL error is thrown from a resolver. 369 + const rawError = unwrapResolverError(error); 370 + 371 + // If the raw error is a GraphQLError (explicitly thrown by our code or 372 + // generated by graphql-js for parse/validation errors), the formattedError 373 + // is already correctly shaped -- pass it through. 374 + if (rawError instanceof GraphQLError) { 375 + return formattedError; 387 376 } 388 377 389 - // In almost all other cases, the error will be an instance of the 390 - // `GraphQLError` class, which apollo instantiates automatically, and uses 391 - // to wrap any non-ApolloError error thrown from a resolver. However, 392 - // ocassionally -- e.g., if an error occurs during context creation rather 393 - // than in the resolver -- the error doesn't get wrapped (or it's wrapped 394 - // but with no originalError), so we handle both cases. Once we have the 395 - // underlying error that was actually thrown, we sanitize it to remove 396 - // sensitive details, and then try to format it in the most informative 397 - // way possible. 398 - const sanitizedError = sanitizeError(e.originalError ?? e); 378 + // For all other errors (CoopError, unexpected errors, context errors), 379 + // sanitize to remove sensitive details and reformat for the client. 380 + const sanitizedError = sanitizeError( 381 + rawError instanceof Error ? rawError : (error as Error), 382 + ); 399 383 const { title: sanitizedErrorTitle, ...extensions } = sanitizedError; 400 384 401 - return { 402 - // When apollo-server wraps the resolver-thrown error in a GraphQLError, 385 + const result: GraphQLFormattedError = { 386 + // When graphql-js wraps the resolver-thrown error in a GraphQLError, 403 387 // it automatically tracks some metadata about where the error was thrown 404 388 // from. That can be useful to clients, in a way that's a bit different 405 389 // from our CoopError.pointer field; it tells them whether a null 406 390 // value was return in the response because a given resolver failed, or 407 391 // because the field's value is actually null. So, we pass this 408 - // apollo-annotated metdata through as-is. 409 - locations: e.locations, 410 - path: e.path, 392 + // metadata through as-is. 393 + locations: formattedError.locations, 394 + path: formattedError.path, 411 395 // Apollo server also defines some predefined error codes that it could 412 396 // be helpful for us to mimic on our custom errors (in case Apollo 413 397 // clients handle them out of the box). The true, Coop-assigned code 414 398 // for the error, though, will be in the `type` key, just like when 415 399 // sending errors in REST responses (though, for GQL, this lives under 416 400 // `extensions`). 417 - code: extensions.type.includes(ErrorType.Unauthenticated) 418 - ? 'UNAUTHENTICATED' 419 - : extensions.type.includes(ErrorType.Unauthorized) 420 - ? 'FORBIDDEN' 421 - : extensions.type.includes(ErrorType.InvalidUserInput) 422 - ? 'BAD_USER_INPUT' 423 - : 'INTERNAL_SERVER_ERROR', 424 - // Then, this is info from the sanitized verion of the actual thrown error. 401 + extensions: { 402 + ...extensions, 403 + code: extensions.type.includes(ErrorType.Unauthenticated) 404 + ? 'UNAUTHENTICATED' 405 + : extensions.type.includes(ErrorType.Unauthorized) 406 + ? 'FORBIDDEN' 407 + : extensions.type.includes(ErrorType.InvalidUserInput) 408 + ? 'BAD_USER_INPUT' 409 + : 'INTERNAL_SERVER_ERROR', 410 + }, 425 411 message: sanitizedErrorTitle, 426 - extensions, 427 412 }; 413 + return result; 428 414 }, 429 415 }); 430 416 431 - await apolloServer.start().then(() => { 432 - apolloServer.applyMiddleware({ app }); 433 - Object.entries(controllers).forEach(([_k, controller]) => { 434 - controller.routes.forEach((it) => { 435 - const handler = it.handler(deps); 436 - app[it.method]( 437 - path.join(controller.pathPrefix, it.path), 438 - ...(Array.isArray(handler) ? handler : [handler]), 439 - ); 440 - }); 441 - }); 417 + await apolloServer.start(); 418 + 419 + app.use( 420 + '/graphql', 421 + express.json(), 422 + expressMiddleware(apolloServer, { 423 + context: async ({ req, res }) => ({ 424 + ...buildContext({ req, res }), 425 + services: makeGqlServices(deps), 426 + dataSources: deps.DataSources, 427 + } as unknown as Context), 428 + }), 429 + ); 442 430 443 - // catch 404 and forward to error handler 444 - app.use(function (_req, _res, next) { 445 - next( 446 - makeNotFoundError('Requested route not found.', { 447 - shouldErrorSpan: true, 448 - }), 431 + Object.entries(controllers).forEach(([_k, controller]) => { 432 + controller.routes.forEach((it) => { 433 + const handler = it.handler(deps); 434 + app[it.method]( 435 + path.join(controller.pathPrefix, it.path), 436 + ...(Array.isArray(handler) ? handler : [handler]), 449 437 ); 450 438 }); 439 + }); 451 440 452 - // error handler 453 - app.use(async function (err, _req, res, _next) { 454 - await deps.Tracer.addActiveSpan( 455 - { resource: 'app', operation: 'handleError' }, 456 - async (span) => { 457 - span.recordException(err); 458 - span.setStatus({ code: SpanStatusCode.ERROR, message: err.message }); 441 + // catch 404 and forward to error handler 442 + app.use(function (_req, _res, next) { 443 + next( 444 + makeNotFoundError('Requested route not found.', { 445 + shouldErrorSpan: true, 446 + }), 447 + ); 448 + }); 459 449 460 - // I don't know if these attributes are necessary, with recordException 461 - span.setAttribute(SEMATTRS_EXCEPTION_MESSAGE, err.message); 462 - if (err.stack) { 463 - span.setAttribute(SEMATTRS_EXCEPTION_STACKTRACE, err.stack); 464 - } 465 - span.setAttribute(SEMATTRS_EXCEPTION_TYPE, err.name); 450 + // error handler 451 + app.use(async function (err, _req, res, _next) { 452 + await deps.Tracer.addActiveSpan( 453 + { resource: 'app', operation: 'handleError' }, 454 + async (span) => { 455 + span.recordException(err); 456 + span.setStatus({ code: SpanStatusCode.ERROR, message: err.message }); 466 457 467 - const errors = (() => { 468 - if (err instanceof AggregateError) { 469 - const extractedErrors = getErrorsFromAggregateError(err); 470 - return isNonEmptyArray(extractedErrors) ? extractedErrors : [err]; 471 - } else { 472 - return [err]; 473 - } 474 - })() satisfies NonEmptyArray<unknown>; 458 + // I don't know if these attributes are necessary, with recordException 459 + span.setAttribute(SEMATTRS_EXCEPTION_MESSAGE, err.message); 460 + if (err.stack) { 461 + span.setAttribute(SEMATTRS_EXCEPTION_STACKTRACE, err.stack); 462 + } 463 + span.setAttribute(SEMATTRS_EXCEPTION_TYPE, err.name); 475 464 476 - // If we had any nested errors (from an AggregateError), 477 - // attach those to the span too. 478 - if (errors.length > 1 || errors[0] !== err) { 479 - span.setAttribute( 480 - 'errors', 481 - jsonStringify( 482 - errors.map((it) => safePick(it, ['name', 'message', 'stack'])), 483 - ), 484 - ); 465 + const errors = (() => { 466 + if (err instanceof AggregateError) { 467 + const extractedErrors = getErrorsFromAggregateError(err); 468 + return isNonEmptyArray(extractedErrors) ? extractedErrors : [err]; 469 + } else { 470 + return [err]; 485 471 } 472 + })() satisfies NonEmptyArray<unknown>; 486 473 487 - // If we've already sent response headers or the response status code, 488 - // we can't actually send a different status code here: it's an error 489 - // in HTTP to send the headers portion of a response twice. So, we 490 - // need to skip this step. 491 - // 492 - // This can happen, e.g., if we have a request handler that 493 - // immediately responds with a 202/204 but then continues to do some 494 - // processing work in the background, and that work errors. 495 - if (!res.headersSent) { 496 - const safeErrors = errors.map((it) => 497 - sanitizeError(it), 498 - ) satisfies SerializableError[] as NonEmptyArray<SerializableError>; 474 + // If we had any nested errors (from an AggregateError), 475 + // attach those to the span too. 476 + if (errors.length > 1 || errors[0] !== err) { 477 + span.setAttribute( 478 + 'errors', 479 + jsonStringify( 480 + errors.map((it) => safePick(it, ['name', 'message', 'stack'])), 481 + ), 482 + ); 483 + } 484 + 485 + // If we've already sent response headers or the response status code, 486 + // we can't actually send a different status code here: it's an error 487 + // in HTTP to send the headers portion of a response twice. So, we 488 + // need to skip this step. 489 + // 490 + // This can happen, e.g., if we have a request handler that 491 + // immediately responds with a 202/204 but then continues to do some 492 + // processing work in the background, and that work errors. 493 + if (!res.headersSent) { 494 + const safeErrors = errors.map((it) => 495 + sanitizeError(it), 496 + ) satisfies SerializableError[] as NonEmptyArray<SerializableError>; 499 497 500 - res.status(pickStatus(safeErrors)).json({ errors: safeErrors }); 501 - } 502 - }, 503 - ); 504 - } as ErrorRequestHandler); 505 - }); 498 + res.status(pickStatus(safeErrors)).json({ errors: safeErrors }); 499 + } 500 + }, 501 + ); 502 + } as ErrorRequestHandler); 506 503 507 504 return { 508 505 app,
-11
server/decs.d.ts
··· 8 8 toLowerCase<T extends string>(this: T): Lowercase<T>; 9 9 } 10 10 11 - // Workaround for https://github.com/apollographql/apollo-server/issues/6868 12 - // At some point, we should just upgrade to Apollo Server 4, but that's a big lift. 13 - // 14 - // We also have to fork + override retry-axios, which we don't depend on 15 - // directly, but it's a dependency of the google maps sdk. However, the version 16 - // of retry-axios used by the SDK isn't compatible with moduleResolution=nodeNext, 17 - // so we were getting a type error. Forking the SDK to update retry-axios isn't 18 - // feasible, because the new version of retry-axios uses a newer axios version, 19 - // which contains some breaking changes (to param serialization; see 20 - // https://github.com/axios/axios/pull/4734), which would be hard to update the 21 - // SDK to account for. 22 11 declare module '@graphql-tools/schema' { 23 12 import { GraphQLSchema } from 'graphql'; 24 13
+5 -4
server/graphql/customScalars/CoopInputOrString.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { GraphQLScalarType, Kind } from 'graphql'; 3 2 4 3 import { CoopInput } from '../../services/moderationConfigService/index.js'; 4 + 5 + import { userInputError } from '../utils/errors.js'; 5 6 6 7 export const CoopInputEnumInverted = Object.fromEntries( 7 8 Object.entries(CoopInput).map(([key, value]) => [value, key]), ··· 20 21 'Either an arbitrary string or a CoopInput enum key name (not the TS runtime value).', 21 22 serialize(value) { 22 23 if (typeof value !== 'string') { 23 - throw new UserInputError('Expected a string.'); 24 + throw userInputError('Expected a string.'); 24 25 } 25 26 26 27 return CoopInputEnumInverted[value] ?? value; ··· 28 29 parseValue: parseCoopInputOrStringValue, 29 30 parseLiteral(ast) { 30 31 if (ast.kind !== Kind.STRING) { 31 - throw new UserInputError('CoopInputOrString must be a string.'); 32 + throw userInputError('CoopInputOrString must be a string.'); 32 33 } 33 34 return parseCoopInputOrStringValue(ast.value); 34 35 }, ··· 36 37 37 38 function parseCoopInputOrStringValue(value: unknown) { 38 39 if (typeof value !== 'string') { 39 - throw new UserInputError('CoopInputOrString must be a CoopInput.'); 40 + throw userInputError('CoopInputOrString must be a CoopInput.'); 40 41 } 41 42 42 43 // @ts-ignore
+5 -4
server/graphql/customScalars/Cursor.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { GraphQLScalarType, Kind } from 'graphql'; 3 2 4 3 import { ··· 10 9 type JsonOf, 11 10 } from '../../utils/encoding.js'; 12 11 import { type JSON } from '../../utils/json-schema-types.js'; 12 + 13 + import { userInputError } from '../utils/errors.js'; 13 14 14 15 /** 15 16 * A cursor is a pointer into a particular place in an ordered collection. ··· 33 34 parseValue: parseCursorValue, 34 35 parseLiteral(ast) { 35 36 if (ast.kind !== Kind.STRING) { 36 - throw new UserInputError('Cursor values must be strings.'); 37 + throw userInputError('Cursor values must be strings.'); 37 38 } 38 39 return parseCursorValue(ast.value); 39 40 }, ··· 41 42 42 43 function parseCursorValue(value: unknown) { 43 44 if (typeof value !== 'string') { 44 - throw new UserInputError('Cursor values must be strings.'); 45 + throw userInputError('Cursor values must be strings.'); 45 46 } 46 47 47 48 try { ··· 49 50 const jsonString = b64Decode(value as B64Of<JsonOf<JSON>>); 50 51 return jsonParse(jsonString); 51 52 } catch { 52 - throw new UserInputError('Invalid cursor value'); 53 + throw userInputError('Invalid cursor value'); 53 54 } 54 55 }
+4 -3
server/graphql/customScalars/NonEmptyString.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { GraphQLScalarType, Kind } from 'graphql'; 3 2 4 3 import { 5 4 tryParseNonEmptyString, 6 5 type NonEmptyString, 7 6 } from '../../utils/typescript-types.js'; 7 + 8 + import { userInputError } from '../utils/errors.js'; 8 9 9 10 /** 10 11 * This scalar is needed for values that must be non-empty strings. This ··· 16 17 description: 'A string that must be non-empty.', 17 18 serialize(value) { 18 19 if (typeof value !== 'string') { 19 - throw new UserInputError('Expected a string.'); 20 + throw userInputError('Expected a string.'); 20 21 } 21 22 return tryParseNonEmptyString(value); 22 23 }, 23 24 parseValue: tryParseNonEmptyString, 24 25 parseLiteral(ast) { 25 26 if (ast.kind !== Kind.STRING) { 26 - throw new UserInputError('NonEmptyString must be a string.'); 27 + throw userInputError('NonEmptyString must be a string.'); 27 28 } 28 29 return tryParseNonEmptyString(ast.value); 29 30 },
+3 -3
server/graphql/customScalars/OpaqueScalarMixin.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { Kind, type GraphQLScalarType } from 'graphql'; 3 2 import jwt from 'jsonwebtoken'; 3 + import { userInputError } from '../utils/errors.js'; 4 4 5 5 const parseOpaqueScalarValue = 6 6 <T>(jwtSigningKey: string) => 7 7 (inputValue: unknown) => { 8 8 if (typeof inputValue !== 'string') { 9 - throw new UserInputError('OpaqueScalar values must be strings.'); 9 + throw userInputError('OpaqueScalar values must be strings.'); 10 10 } 11 11 12 12 return jwt.verify(inputValue, jwtSigningKey) as T; ··· 50 50 parseValue: parseOpaqueScalarValue<T>(jwtSigningKey), 51 51 parseLiteral(ast) { 52 52 if (ast.kind !== Kind.STRING) { 53 - throw new UserInputError('OpaqueScalar values must be strings.'); 53 + throw userInputError('OpaqueScalar values must be strings.'); 54 54 } 55 55 return parseOpaqueScalarValue<T>(jwtSigningKey)(ast.value); 56 56 },
+5 -6
server/graphql/customScalars/StringOrFloat.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { GraphQLScalarType, Kind } from 'graphql'; 2 + 3 + import { userInputError } from '../utils/errors.js'; 3 4 4 5 /** 5 6 * This scalar is needed for values that can be represented either ··· 10 11 description: 'Either an arbitrary string or a float.', 11 12 serialize(value) { 12 13 if (typeof value !== 'string' && typeof value !== 'number') { 13 - throw new UserInputError('Expected a string or float.'); 14 + throw userInputError('Expected a string or float.'); 14 15 } 15 16 return value; 16 17 }, ··· 21 22 ast.kind !== Kind.FLOAT && 22 23 ast.kind !== Kind.INT 23 24 ) { 24 - throw new UserInputError('StringOrFloat must be a string or number.'); 25 + throw userInputError('StringOrFloat must be a string or number.'); 25 26 } 26 27 return parseStringOrFloatValue(ast.value); 27 28 }, ··· 29 30 30 31 function parseStringOrFloatValue(value: unknown) { 31 32 if (typeof value !== 'string' && typeof value !== 'number') { 32 - throw new UserInputError( 33 - 'StringOrFloat must be a string or number when passed to the server.', 34 - ); 33 + throw userInputError('StringOrFloat must be a string or number when passed to the server.'); 35 34 } 36 35 37 36 // NB: Number('') returns 0, so we have to check for the empty string
+1 -3
server/graphql/datasources/ActionApi.ts
··· 1 1 import { type Exception } from '@opentelemetry/api'; 2 - import { DataSource } from 'apollo-datasource'; 3 2 import pLimit from 'p-limit'; 4 3 import { uid } from 'uid'; 5 4 import { v1 as uuidv1 } from 'uuid'; ··· 30 29 /** 31 30 * GraphQL Object for an Action 32 31 */ 33 - class ActionAPI extends DataSource { 32 + class ActionAPI { 34 33 constructor( 35 34 private readonly actionPublisher: Dependencies['ActionPublisher'], 36 35 private readonly sequelize: Dependencies['Sequelize'], ··· 38 37 private readonly itemInvestigationService: Dependencies['ItemInvestigationService'], 39 38 private readonly getItemTypeEventuallyConsistent: Dependencies['getItemTypeEventuallyConsistent'], 40 39 ) { 41 - super(); 42 40 } 43 41 44 42 async getGraphQLActionFromId(opts: { id: string; orgId: string }) {
+1 -3
server/graphql/datasources/IntegrationApi.ts
··· 1 - import { DataSource } from 'apollo-datasource'; 2 1 3 2 import { inject, type Dependencies } from '../../iocContainer/index.js'; 4 3 import '../../services/signalAuthService/index.js'; ··· 62 61 /** 63 62 * TODO: this whole class should probably be merged into the signal auth service. 64 63 */ 65 - class IntegrationAPI extends DataSource { 64 + class IntegrationAPI { 66 65 constructor( 67 66 private readonly signalAuthService: Dependencies['SignalAuthService'], 68 67 ) { 69 - super(); 70 68 } 71 69 72 70 async setConfig(
+1 -3
server/graphql/datasources/InvestigationApi.ts
··· 1 - import { DataSource } from 'apollo-datasource'; 2 1 3 2 import { inject, type Dependencies } from '../../iocContainer/index.js'; 4 3 import { type ItemSubmissionForGQL } from '../types.js'; ··· 8 7 user: ItemSubmissionForGQL; 9 8 }; 10 9 11 - class InvestigationAPI extends DataSource { 10 + class InvestigationAPI { 12 11 constructor( 13 12 private readonly itemHistoryQueries: Dependencies['ItemHistoryQueries'], 14 13 ) { 15 - super(); 16 14 } 17 15 18 16 async getItemHistory(opts: {
+1 -3
server/graphql/datasources/LocationBankApi.ts
··· 1 1 import { type Exception } from '@opentelemetry/api'; 2 - import { DataSource } from 'apollo-datasource'; 3 2 import { uid } from 'uid'; 4 3 import { v1 as uuidV1 } from 'uuid'; 5 4 ··· 28 27 'fullPlacesApiResponse' 29 28 >; 30 29 31 - class LocationBankAPI extends DataSource { 30 + class LocationBankAPI { 32 31 private lookupPlaceId: PlacesApiService['lookupPlaceId']; 33 32 constructor( 34 33 placesApiService: PlacesApiService, 35 34 private readonly sequelize: Dependencies['Sequelize'], 36 35 private readonly tracer: Dependencies['Tracer'], 37 36 ) { 38 - super(); 39 37 this.lookupPlaceId = placesApiService.lookupPlaceId.bind(placesApiService); 40 38 } 41 39
+1 -3
server/graphql/datasources/OrgApi.ts
··· 1 1 import crypto from 'node:crypto'; 2 2 import { URL } from 'node:url'; 3 3 import { type Exception } from '@opentelemetry/api'; 4 - import { DataSource } from 'apollo-datasource'; 5 4 import { uid } from 'uid'; 6 5 7 6 import { inject, type Dependencies } from '../../iocContainer/index.js'; ··· 20 19 type GQLRequestDemoInput, 21 20 } from '../generated.js'; 22 21 23 - class OrgAPI extends DataSource { 22 + class OrgAPI { 24 23 constructor( 25 24 private readonly orgCreationLogger: Dependencies['OrgCreationLogger'], 26 25 private readonly apiKeyService: Dependencies['ApiKeyService'], ··· 34 33 private readonly orgSettingsService: Dependencies['OrgSettingsService'], 35 34 private readonly manualReviewToolService: Dependencies['ManualReviewToolService'], 36 35 ) { 37 - super(); 38 36 } 39 37 40 38 async createOrg(params: GQLMutationCreateOrgArgs) {
+4 -7
server/graphql/datasources/RuleApi.ts
··· 2 2 3 3 import { type Exception } from '@opentelemetry/api'; 4 4 import { makeEnumLike } from '@roostorg/types'; 5 - import { DataSource } from 'apollo-datasource'; 6 - import { AuthenticationError } from 'apollo-server-express'; 5 + 7 6 import { sql, type Kysely } from 'kysely'; 8 7 import Sequelize from 'sequelize'; 9 8 import { uid } from 'uid'; ··· 68 67 import { oneOfInputToTaggedUnion } from '../utils/inputHelpers.js'; 69 68 import { type CursorInfo, type Edge } from '../utils/paginationHandler.js'; 70 69 import { locationAreaInputToLocationArea } from './LocationBankApi.js'; 70 + import { unauthenticatedError } from '../utils/errors.js'; 71 71 72 72 const { Op, Transaction } = Sequelize; 73 73 const SortOrder = makeEnumLike(['ASC', 'DESC']); ··· 267 267 /** 268 268 * GraphQL Object for a Rule 269 269 */ 270 - class RuleAPI extends DataSource { 270 + class RuleAPI { 271 271 private readonly warehouse: Kysely<DataWarehousePublicSchema>; 272 272 273 273 constructor( ··· 279 279 private readonly tracer: Dependencies['Tracer'], 280 280 private readonly signalsService: Dependencies['SignalsService'], 281 281 ) { 282 - super(); 283 282 this.warehouse = dialect.getKyselyInstance() as Kysely<DataWarehousePublicSchema>; 284 283 } 285 284 286 285 async getGraphQLRuleFromId(id: string, orgId: string) { 287 286 const rule = await this.models.Rule.findByPk(id, { rejectOnEmpty: true }); 288 287 if (rule.orgId !== orgId) { 289 - throw new AuthenticationError( 290 - 'User not authenticated to fetch this rule', 291 - ); 288 + throw unauthenticatedError('User not authenticated to fetch this rule'); 292 289 } 293 290 294 291 return rule;
+1 -3
server/graphql/datasources/UserApi.ts
··· 1 1 /* eslint-disable max-classes-per-file */ 2 2 3 3 import { type Exception } from '@opentelemetry/api'; 4 - import { DataSource } from 'apollo-datasource'; 5 4 import { type PassportContext } from 'graphql-passport'; 6 5 import { uid } from 'uid'; 7 6 ··· 23 22 /** 24 23 * GraphQL Object for a User 25 24 */ 26 - class UserAPI extends DataSource { 25 + class UserAPI { 27 26 constructor( 28 27 private readonly sequelize: Dependencies['Sequelize'], 29 28 private readonly tracer: Dependencies['Tracer'], 30 29 private readonly userManagementService: Dependencies['UserManagementService'], 31 30 ) { 32 - super(); 33 31 } 34 32 35 33 async getGraphQLUserFromId(opts: { id: string; orgId: string }) {
+10 -11
server/graphql/modules/action.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { isCoopErrorOfType } from '../../utils/errors.js'; 4 2 import { assertUnreachable } from '../../utils/misc.js'; 5 3 import { ··· 12 10 type GQLQueryResolvers, 13 11 } from '../generated.js'; 14 12 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 13 + import { unauthenticatedError } from '../utils/errors.js'; 15 14 16 15 const typeDefs = /* GraphQL */ ` 17 16 interface ActionBase { ··· 183 182 async itemTypes(action, _, context) { 184 183 const user = context.getUser(); 185 184 if (user == null) { 186 - throw new AuthenticationError('User required.'); 185 + throw unauthenticatedError('User required.'); 187 186 } 188 187 return context.services.ModerationConfigService.getItemTypesForAction({ 189 188 orgId: user.orgId, ··· 196 195 async itemTypes(action, _, context) { 197 196 const user = context.getUser(); 198 197 if (user == null) { 199 - throw new AuthenticationError('User required.'); 198 + throw unauthenticatedError('User required.'); 200 199 } 201 200 return context.services.ModerationConfigService.getItemTypesForAction({ 202 201 orgId: user.orgId, ··· 209 208 async itemTypes(action, _, context) { 210 209 const user = context.getUser(); 211 210 if (user == null) { 212 - throw new AuthenticationError('User required.'); 211 + throw unauthenticatedError('User required.'); 213 212 } 214 213 return context.services.ModerationConfigService.getItemTypesForAction({ 215 214 orgId: user.orgId, ··· 222 221 async itemTypes(action, _, context) { 223 222 const user = context.getUser(); 224 223 if (user == null) { 225 - throw new AuthenticationError('User required.'); 224 + throw unauthenticatedError('User required.'); 226 225 } 227 226 return context.services.ModerationConfigService.getItemTypesForAction({ 228 227 orgId: user.orgId, ··· 235 234 async action(_, { id }, context) { 236 235 const user = context.getUser(); 237 236 if (user == null) { 238 - throw new AuthenticationError('User required.'); 237 + throw unauthenticatedError('User required.'); 239 238 } 240 239 241 240 return context.dataSources.actionAPI.getGraphQLActionFromId({ ··· 250 249 try { 251 250 const user = context.getUser(); 252 251 if (user == null) { 253 - throw new AuthenticationError('User required.'); 252 + throw unauthenticatedError('User required.'); 254 253 } 255 254 const action = await context.dataSources.actionAPI.createAction( 256 255 params.input, ··· 269 268 try { 270 269 const user = context.getUser(); 271 270 if (user == null) { 272 - throw new AuthenticationError('User required.'); 271 + throw unauthenticatedError('User required.'); 273 272 } 274 273 const { orgId } = user; 275 274 const action = await context.dataSources.actionAPI.updateAction( ··· 288 287 async deleteAction(_, params, context) { 289 288 const user = context.getUser(); 290 289 if (user == null) { 291 - throw new AuthenticationError('User required.'); 290 + throw unauthenticatedError('User required.'); 292 291 } 293 292 const { orgId } = user; 294 293 return context.dataSources.actionAPI.deleteAction(orgId, params.id); ··· 296 295 async bulkExecuteActions(_, params, context) { 297 296 const user = context.getUser(); 298 297 if (user == null) { 299 - throw new AuthenticationError('User required.'); 298 + throw unauthenticatedError('User required.'); 300 299 } 301 300 302 301 const { orgId, id, email } = user;
+5 -5
server/graphql/modules/actionStatistics.ts
··· 1 1 /* eslint-disable max-lines */ 2 2 3 - import { AuthenticationError } from 'apollo-server-core'; 4 - 5 3 import { type GQLQueryResolvers } from '../generated.js'; 4 + 5 + import { unauthenticatedError } from '../utils/errors.js'; 6 6 7 7 const typeDefs = /* GraphQL */ ` 8 8 type ActionData { ··· 90 90 async actionStatistics(_, { input }, context) { 91 91 const user = context.getUser(); 92 92 if (user == null) { 93 - throw new AuthenticationError('Authenticated user required'); 93 + throw unauthenticatedError('Authenticated user required'); 94 94 } 95 95 96 96 const a = { ··· 122 122 async topPolicyViolations(_, { input }, context) { 123 123 const user = context.getUser(); 124 124 if (user == null) { 125 - throw new AuthenticationError('Authenticated user required'); 125 + throw unauthenticatedError('Authenticated user required'); 126 126 } 127 127 128 128 try { ··· 149 149 async recentUserStrikeActions(_, { input }, context) { 150 150 const user = context.getUser(); 151 151 if (user == null) { 152 - throw new AuthenticationError('Authenticated user required'); 152 + throw unauthenticatedError('Authenticated user required'); 153 153 } 154 154 const recentUserStrikeActions = 155 155 await context.services.UserStrikeService.getRecentUserStrikeActions({
+6 -11
server/graphql/modules/apiKey.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { ErrorType, CoopError } from '../../utils/errors.js'; 4 2 import { logErrorJson } from '../../utils/logging.js'; 5 3 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 4 + import { forbiddenError } from '../utils/errors.js'; 6 5 7 6 /** Context shape required by rotateWebhookSigningKey (avoids importing resolvers). */ 8 7 type RotateWebhookSigningKeyContext = { ··· 78 77 async apiKey(_: any, __: any, context: any) { 79 78 const user = context.getUser(); 80 79 if (!user || !user.orgId) { 81 - throw new AuthenticationError('User must be authenticated'); 80 + throw forbiddenError('User does not have permission to check if key exists'); 82 81 } 83 82 84 83 const apiKeyRecord = await context.services.ApiKeyService.getActiveApiKeyForOrg(user.orgId); ··· 94 93 async rotateApiKey(_: any, { input }: any, context: any) { 95 94 const user = context.getUser(); 96 95 if (!user || !user.orgId) { 97 - throw new AuthenticationError('User must be authenticated'); 96 + throw forbiddenError('User does not have permission to rotate the API key'); 98 97 } 99 98 if (!user.getPermissions().includes('MANAGE_ORG')) { 100 - throw new AuthenticationError( 101 - 'User does not have permission to rotate the API key', 102 - ); 99 + throw forbiddenError('User does not have permission to rotate the API key'); 103 100 } 104 101 105 102 try { ··· 148 145 ) { 149 146 const user = context.getUser(); 150 147 if (!user || !user.orgId) { 151 - throw new AuthenticationError('User must be authenticated'); 148 + throw forbiddenError('User does not have permission to rotate the webhook signing key'); 152 149 } 153 150 if (!user.getPermissions().includes('MANAGE_ORG')) { 154 - throw new AuthenticationError( 155 - 'User does not have permission to rotate the webhook signing key', 156 - ); 151 + throw forbiddenError('User does not have permission to rotate the webhook signing key'); 157 152 } 158 153 159 154 try {
+4 -5
server/graphql/modules/backtest.ts
··· 1 - import { AuthenticationError, ForbiddenError } from 'apollo-server-express'; 2 - 3 1 import { type Backtest } from '../../models/rules/BacktestModel.js'; 4 2 import { 5 3 hasPermission, ··· 12 10 makeConnectionResolver, 13 11 type ConnectionArguments, 14 12 } from '../utils/paginationHandler.js'; 13 + import { forbiddenError, unauthenticatedError } from '../utils/errors.js'; 15 14 16 15 const typeDefs = /* GraphQL */ ` 17 16 enum BacktestStatus { ··· 119 118 ); 120 119 121 120 if (user == null) { 122 - throw new AuthenticationError('Authenticated user required'); 121 + throw unauthenticatedError('Authenticated user required'); 123 122 } else if (!hasPermission(UserPermission.RUN_BACKTEST, user.role)) { 124 - throw new ForbiddenError('User not authorized to create backtests.'); 123 + throw forbiddenError('User not authorized to create backtests.'); 125 124 } else if (!rule || user.orgId !== rule.orgId) { 126 - throw new ForbiddenError('Invalid rule.'); 125 + throw forbiddenError('Invalid rule.'); 127 126 } 128 127 129 128 return {
+10 -10
server/graphql/modules/hashBanks/resolvers.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 1 import { isCoopErrorOfType } from '../../../utils/errors.js'; 3 2 import type { Context } from '../../resolvers.js'; 4 3 import type { GQLMutationResolvers, GQLQueryResolvers } from '../../generated.js'; 5 4 import { gqlErrorResult, gqlSuccessResult } from '../../utils/gqlResult.js'; 5 + import { unauthenticatedError } from '../../utils/errors.js'; 6 6 7 7 interface ExchangeConfigInput { 8 8 api_name: string; ··· 14 14 async hashBanks(_: unknown, __: unknown, context: Context) { 15 15 const user = context.getUser(); 16 16 if (!user?.orgId) { 17 - throw new AuthenticationError('User required.'); 17 + throw unauthenticatedError('User required.'); 18 18 } 19 19 20 20 return context.services.HMAHashBankService.listBanks(user.orgId); ··· 23 23 async hashBank(_: unknown, { name }: { name: string }, context: Context) { 24 24 const user = context.getUser(); 25 25 if (!user?.orgId) { 26 - throw new AuthenticationError('User required.'); 26 + throw unauthenticatedError('User required.'); 27 27 } 28 28 29 29 try { ··· 39 39 async hashBankById(_: unknown, { id }: { id: string }, context: Context) { 40 40 const user = context.getUser(); 41 41 if (!user?.orgId) { 42 - throw new AuthenticationError('User required.'); 42 + throw unauthenticatedError('User required.'); 43 43 } 44 44 45 45 try { ··· 55 55 async exchangeApis(_: unknown, __: unknown, context: Context) { 56 56 const user = context.getUser(); 57 57 if (!user?.orgId) { 58 - throw new AuthenticationError('User required.'); 58 + throw unauthenticatedError('User required.'); 59 59 } 60 60 61 61 return context.services.HMAHashBankService.getExchangeApis(); ··· 64 64 async exchangeApiSchema(_: unknown, { apiName }: { apiName: string }, context: Context) { 65 65 const user = context.getUser(); 66 66 if (!user?.orgId) { 67 - throw new AuthenticationError('User required.'); 67 + throw unauthenticatedError('User required.'); 68 68 } 69 69 70 70 return context.services.HMAHashBankService.getExchangeApiSchema(apiName); ··· 84 84 ) { 85 85 const user = context.getUser(); 86 86 if (!user?.orgId) { 87 - throw new AuthenticationError('User required.'); 87 + throw unauthenticatedError('User required.'); 88 88 } 89 89 90 90 try { ··· 136 136 ) { 137 137 const user = context.getUser(); 138 138 if (!user?.orgId) { 139 - throw new AuthenticationError('User required.'); 139 + throw unauthenticatedError('User required.'); 140 140 } 141 141 142 142 try { ··· 165 165 ) { 166 166 const user = context.getUser(); 167 167 if (!user?.orgId) { 168 - throw new AuthenticationError('User required.'); 168 + throw unauthenticatedError('User required.'); 169 169 } 170 170 171 171 await context.services.HMAHashBankService.deleteBank(user.orgId, id); ··· 179 179 ) { 180 180 const user = context.getUser(); 181 181 if (!user?.orgId) { 182 - throw new AuthenticationError('User required.'); 182 + throw unauthenticatedError('User required.'); 183 183 } 184 184 185 185 // eslint-disable-next-line no-restricted-syntax
+1 -3
server/graphql/modules/hashBanks/schema.ts
··· 1 - import { gql } from 'apollo-server-express'; 2 - 3 - export const typeDefs = gql` 1 + export const typeDefs = /* GraphQL */ ` 4 2 type ExchangeInfo { 5 3 api: String! 6 4 enabled: Boolean!
+11 -11
server/graphql/modules/insights.ts
··· 1 - import { AuthenticationError } from 'apollo-server-core'; 2 1 // because graphql args are sometimes imported wiht _, we use 3 2 // lodash as opposed to _ to avoid overloading 4 3 import lodash from 'lodash'; ··· 14 13 GQLRuleInsightsResolvers, 15 14 } from '../generated.js'; 16 15 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 16 + import { unauthenticatedError } from '../utils/errors.js'; 17 17 18 18 const typeDefs = /* GraphQL */ ` 19 19 enum LookbackVersion { ··· 123 123 async signalResults(ruleExecutionResult, _, context) { 124 124 const user = context.getUser(); 125 125 if (user == null) { 126 - throw new AuthenticationError('Authenticated user required'); 126 + throw unauthenticatedError('Authenticated user required'); 127 127 } 128 128 129 129 const fullResults = ruleExecutionResult.result; ··· 139 139 async signalResults(reportingRuleExecutionResult, _, context) { 140 140 const user = context.getUser(); 141 141 if (user == null) { 142 - throw new AuthenticationError('Authenticated user required'); 142 + throw unauthenticatedError('Authenticated user required'); 143 143 } 144 144 145 145 const fullResults = reportingRuleExecutionResult.result; ··· 155 155 async passRateData(rule, args, context) { 156 156 const user = context.getUser(); 157 157 if (user == null) { 158 - throw new AuthenticationError('Authenticated user required'); 158 + throw unauthenticatedError('Authenticated user required'); 159 159 } 160 160 161 161 return context.dataSources.ruleAPI.ruleInsights.getRulePassRateData( ··· 167 167 async latestVersionSamples(rule, _args, context) { 168 168 const user = context.getUser(); 169 169 if (user == null) { 170 - throw new AuthenticationError('Authenticated user required'); 170 + throw unauthenticatedError('Authenticated user required'); 171 171 } 172 172 173 173 const samples = ··· 197 197 async priorVersionSamples(rule, _args, context) { 198 198 const user = context.getUser(); 199 199 if (user == null) { 200 - throw new AuthenticationError('Authenticated user required'); 200 + throw unauthenticatedError('Authenticated user required'); 201 201 } 202 202 203 203 const samples = ··· 230 230 async passRateData(rule, args, context) { 231 231 const user = context.getUser(); 232 232 if (user == null) { 233 - throw new AuthenticationError('Authenticated user required'); 233 + throw unauthenticatedError('Authenticated user required'); 234 234 } 235 235 236 236 return context.dataSources.ruleAPI.ruleInsights.getRulePassRateData( ··· 242 242 async latestVersionSamples(rule, _args, context) { 243 243 const user = context.getUser(); 244 244 if (user == null) { 245 - throw new AuthenticationError('Authenticated user required'); 245 + throw unauthenticatedError('Authenticated user required'); 246 246 } 247 247 248 248 const samples = ··· 272 272 async priorVersionSamples(rule, _args, context) { 273 273 const user = context.getUser(); 274 274 if (user == null) { 275 - throw new AuthenticationError('Authenticated user required'); 275 + throw unauthenticatedError('Authenticated user required'); 276 276 } 277 277 278 278 const samples = ··· 305 305 async getUserStrikeCountDistribution(_, __, context) { 306 306 const user = context.getUser(); 307 307 if (user == null) { 308 - throw new AuthenticationError('Authenticated user required'); 308 + throw unauthenticatedError('Authenticated user required'); 309 309 } 310 310 311 311 const allUserStrikeCounts = ··· 324 324 async getFullRuleResultForItem(_, { input }, context) { 325 325 const user = context.getUser(); 326 326 if (user == null) { 327 - throw new AuthenticationError('Authenticated user required'); 327 + throw unauthenticatedError('Authenticated user required'); 328 328 } 329 329 const { ruleId, item, date, lookback } = input; 330 330 try {
+5 -6
server/graphql/modules/integration.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { getIntegrationRegistry } from '../../services/integrationRegistry/index.js'; 4 2 import { Integration } from '../../services/signalsService/index.js'; 5 3 import { isCoopErrorOfType } from '../../utils/errors.js'; ··· 15 13 } from '../generated.js'; 16 14 import { type ResolverMap } from '../resolvers.js'; 17 15 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 16 + import { unauthenticatedError } from '../utils/errors.js'; 18 17 19 18 const typeDefs = /* GraphQL */ ` 20 19 enum Integration { ··· 219 218 try { 220 219 const user = context.getUser(); 221 220 if (user == null) { 222 - throw new AuthenticationError('Unauthenticated User'); 221 + throw unauthenticatedError('Unauthenticated User'); 223 222 } 224 223 225 224 if (!getIntegrationRegistry().has(name)) { ··· 251 250 async availableIntegrations(_, __, context) { 252 251 const user = context.getUser(); 253 252 if (user == null) { 254 - throw new AuthenticationError('Unauthenticated User'); 253 + throw unauthenticatedError('Unauthenticated User'); 255 254 } 256 255 return context.dataSources.integrationAPI.getAvailableIntegrations() as GQLIntegrationMetadata[]; 257 256 }, ··· 268 267 try { 269 268 const user = context.getUser(); 270 269 if (user == null) { 271 - throw new AuthenticationError('Unauthenticated User'); 270 + throw unauthenticatedError('Unauthenticated User'); 272 271 } 273 272 const newConfig = await context.dataSources.integrationAPI.setConfig( 274 273 params.input, ··· 297 296 try { 298 297 const user = context.getUser(); 299 298 if (user == null) { 300 - throw new AuthenticationError('Unauthenticated User'); 299 + throw unauthenticatedError('Unauthenticated User'); 301 300 } 302 301 const newConfig = 303 302 await context.dataSources.integrationAPI.setConfigByIntegrationId(
+14 -13
server/graphql/modules/investigation.ts
··· 1 1 /* eslint-disable max-lines */ 2 2 import { type DateString } from '@roostorg/types'; 3 - import { AuthenticationError } from 'apollo-server-express'; 3 + 4 4 import _ from 'lodash'; 5 5 6 6 import { type ConditionSetWithResult } from '../../models/rules/RuleModel.js'; ··· 25 25 } from '../generated.js'; 26 26 import { formatItemSubmissionForGQL } from '../types.js'; 27 27 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 28 + import { unauthenticatedError } from '../utils/errors.js'; 28 29 29 30 const typeDefs = /* GraphQL */ ` 30 31 type Query { ··· 130 131 async executions(it, _, context) { 131 132 const user = context.getUser(); 132 133 if (user == null) { 133 - throw new AuthenticationError('Unauthenticated User'); 134 + throw unauthenticatedError('Unauthenticated User'); 134 135 } 135 136 136 137 const rows = ··· 151 152 async actions(it, _, context) { 152 153 const user = context.getUser(); 153 154 if (user == null) { 154 - throw new AuthenticationError('Unauthenticated User'); 155 + throw unauthenticatedError('Unauthenticated User'); 155 156 } 156 157 157 158 const actions = ··· 165 166 async submissions(it, _, context) { 166 167 const user = context.getUser(); 167 168 if (user == null) { 168 - throw new AuthenticationError('Unauthenticated User'); 169 + throw unauthenticatedError('Unauthenticated User'); 169 170 } 170 171 171 172 const submissions = ··· 182 183 async itemSubmissions(_, { itemIdentifiers }, context) { 183 184 const user = context.getUser(); 184 185 if (user == null) { 185 - throw new AuthenticationError('Unauthenticated User'); 186 + throw unauthenticatedError('Unauthenticated User'); 186 187 } 187 188 188 189 const items = await Promise.all( ··· 205 206 async latestItemSubmissions(_, { itemIdentifiers }, context) { 206 207 const user = context.getUser(); 207 208 if (user == null) { 208 - throw new AuthenticationError('Unauthenticated User'); 209 + throw unauthenticatedError('Unauthenticated User'); 209 210 } 210 211 211 212 const items = await Promise.all( ··· 224 225 async userHistory(_, { itemIdentifier }, context) { 225 226 const user = context.getUser(); 226 227 if (user == null) { 227 - throw new AuthenticationError('Unauthenticated User'); 228 + throw unauthenticatedError('Unauthenticated User'); 228 229 } 229 230 230 231 try { ··· 259 260 async itemsWithId(_, { itemId, typeId, returnFirstResultOnly }, context) { 260 261 const user = context.getUser(); 261 262 if (user == null) { 262 - throw new AuthenticationError('Unauthenticated User'); 263 + throw unauthenticatedError('Unauthenticated User'); 263 264 } 264 265 265 266 if (typeId) { ··· 335 336 const { id: itemId, typeId } = itemIdentifier; 336 337 const user = context.getUser(); 337 338 if (user == null) { 338 - throw new AuthenticationError('Unauthenticated User'); 339 + throw unauthenticatedError('Unauthenticated User'); 339 340 } 340 341 341 342 const item = ··· 378 379 async threadHistory(_, { threadIdentifier, endDate }, context) { 379 380 const user = context.getUser(); 380 381 if (user == null) { 381 - throw new AuthenticationError('Unauthenticated User'); 382 + throw unauthenticatedError('Unauthenticated User'); 382 383 } 383 384 const threadSubmissions = await asyncIterableToArray( 384 385 context.services.ItemInvestigationService.getThreadSubmissionsByTime({ ··· 415 416 ) { 416 417 const user = context.getUser(); 417 418 if (user == null) { 418 - throw new AuthenticationError('Unauthenticated User'); 419 + throw unauthenticatedError('Unauthenticated User'); 419 420 } 420 421 const items = await asyncIterableToArray( 421 422 context.services.ItemInvestigationService.getItemSubmissionsByCreator({ ··· 446 447 async latestItemsCreatedByWithThread(__, { itemIdentifier }, context) { 447 448 const user = context.getUser(); 448 449 if (user == null) { 449 - throw new AuthenticationError('Unauthenticated User'); 450 + throw unauthenticatedError('Unauthenticated User'); 450 451 } 451 452 452 453 const items = await asyncIterableToArray( ··· 555 556 async itemActionHistory(_, { itemIdentifier, submissionTime }, context) { 556 557 const user = context.getUser(); 557 558 if (user == null) { 558 - throw new AuthenticationError('Unauthenticated User'); 559 + throw unauthenticatedError('Unauthenticated User'); 559 560 } 560 561 return context.services.ItemInvestigationService.getItemActionHistory({ 561 562 orgId: user.orgId,
+21 -21
server/graphql/modules/itemType.ts
··· 7 7 type FieldType, 8 8 type ScalarType, 9 9 } from '@roostorg/types'; 10 - import { AuthenticationError } from 'apollo-server-core'; 11 10 12 11 import { 13 12 type ContentItemType as ContentItemTypeT, ··· 37 36 import { type Context } from '../resolvers.js'; 38 37 import { formatItemSubmissionForGQL } from '../types.js'; 39 38 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 39 + import { unauthenticatedError } from '../utils/errors.js'; 40 40 41 41 export type ItemTypeResolversParentType = ItemTypeT | ItemTypeSelector; 42 42 export type ThreadItemTypeResolversParentType = ··· 533 533 async userScore(userItem, __, context) { 534 534 const user = context.getUser(); 535 535 if (!user) { 536 - throw new AuthenticationError('User required.'); 536 + throw unauthenticatedError('User required.'); 537 537 } 538 538 539 539 const { id, type } = userItem; ··· 551 551 async baseFields(it, _, context) { 552 552 const user = context.getUser(); 553 553 if (!user) { 554 - throw new AuthenticationError('User required.'); 554 + throw unauthenticatedError('User required.'); 555 555 } 556 556 const itemType = await getItemTypeFromItemTypeOrSelector(it, context); 557 557 return itemType.schema; ··· 559 559 async derivedFields(itemTypeOrSelector, _, context) { 560 560 const user = context.getUser(); 561 561 if (!user) { 562 - throw new AuthenticationError('User required.'); 562 + throw unauthenticatedError('User required.'); 563 563 } 564 564 const itemType = await getItemTypeFromItemTypeOrSelector( 565 565 itemTypeOrSelector, ··· 574 574 async name(itemTypeOrSelector, _, context) { 575 575 const user = context.getUser(); 576 576 if (!user) { 577 - throw new AuthenticationError('User required.'); 577 + throw unauthenticatedError('User required.'); 578 578 } 579 579 const itemType = await getItemTypeFromItemTypeOrSelector( 580 580 itemTypeOrSelector, ··· 585 585 async description(itemTypeOrSelector, _, context) { 586 586 const user = context.getUser(); 587 587 if (!user) { 588 - throw new AuthenticationError('User required.'); 588 + throw unauthenticatedError('User required.'); 589 589 } 590 590 const itemType = await getItemTypeFromItemTypeOrSelector( 591 591 itemTypeOrSelector, ··· 596 596 async version(itemTypeOrSelector, _, context) { 597 597 const user = context.getUser(); 598 598 if (!user) { 599 - throw new AuthenticationError('User required.'); 599 + throw unauthenticatedError('User required.'); 600 600 } 601 601 const itemType = await getItemTypeFromItemTypeOrSelector( 602 602 itemTypeOrSelector, ··· 607 607 async schemaVariant(itemTypeOrSelector, _, context) { 608 608 const user = context.getUser(); 609 609 if (!user) { 610 - throw new AuthenticationError('User required.'); 610 + throw unauthenticatedError('User required.'); 611 611 } 612 612 const itemType = await getItemTypeFromItemTypeOrSelector( 613 613 itemTypeOrSelector, ··· 618 618 async hiddenFields(itemTypeOrSelector, _, context) { 619 619 const user = context.getUser(); 620 620 if (!user) { 621 - throw new AuthenticationError('User required.'); 621 + throw unauthenticatedError('User required.'); 622 622 } 623 623 624 624 return context.services.ManualReviewToolService.getHiddenFieldsForItemType({ ··· 656 656 async isDefaultUserType(itemTypeOrSelector, _, context) { 657 657 const user = context.getUser(); 658 658 if (!user) { 659 - throw new AuthenticationError('User required.'); 659 + throw unauthenticatedError('User required.'); 660 660 } 661 661 const itemType = await getItemTypeFromItemTypeOrSelector( 662 662 itemTypeOrSelector, ··· 675 675 async itemType(_, { id, version }, context) { 676 676 const user = context.getUser(); 677 677 if (!user) { 678 - throw new AuthenticationError('User required.'); 678 + throw unauthenticatedError('User required.'); 679 679 } 680 680 681 681 return ( ··· 691 691 async itemTypes(_, { identifiers }, context) { 692 692 const user = context.getUser(); 693 693 if (!user) { 694 - throw new AuthenticationError('User required.'); 694 + throw unauthenticatedError('User required.'); 695 695 } 696 696 697 697 return filterNullOrUndefined( ··· 713 713 try { 714 714 const user = context.getUser(); 715 715 if (!user) { 716 - throw new AuthenticationError('User required.'); 716 + throw unauthenticatedError('User required.'); 717 717 } 718 718 719 719 const partialItemSubmissions = ··· 746 746 async createContentItemType(__, params, context) { 747 747 const user = context.getUser(); 748 748 if (user == null) { 749 - throw new AuthenticationError('User required.'); 749 + throw unauthenticatedError('User required.'); 750 750 } 751 751 752 752 const { fields, hiddenFields, fieldRoles } = params.input; ··· 784 784 async updateContentItemType(_, params, context) { 785 785 const user = context.getUser(); 786 786 if (user == null) { 787 - throw new AuthenticationError('User required.'); 787 + throw unauthenticatedError('User required.'); 788 788 } 789 789 790 790 const { id, name, description, fields, hiddenFields, fieldRoles } = ··· 830 830 async createThreadItemType(__, params, context) { 831 831 const user = context.getUser(); 832 832 if (user == null) { 833 - throw new AuthenticationError('User required.'); 833 + throw unauthenticatedError('User required.'); 834 834 } 835 835 836 836 const { fields, hiddenFields, fieldRoles } = params.input; ··· 866 866 async updateThreadItemType(_, params, context) { 867 867 const user = context.getUser(); 868 868 if (user == null) { 869 - throw new AuthenticationError('User required.'); 869 + throw unauthenticatedError('User required.'); 870 870 } 871 871 872 872 const { id, name, description, fields, hiddenFields, fieldRoles } = ··· 910 910 async createUserItemType(__, params, context) { 911 911 const user = context.getUser(); 912 912 if (user == null) { 913 - throw new AuthenticationError('User required.'); 913 + throw unauthenticatedError('User required.'); 914 914 } 915 915 916 916 const { fields, hiddenFields, fieldRoles } = params.input; ··· 949 949 async updateUserItemType(_, params, context) { 950 950 const user = context.getUser(); 951 951 if (user == null) { 952 - throw new AuthenticationError('User required.'); 952 + throw unauthenticatedError('User required.'); 953 953 } 954 954 955 955 const { id, name, description, fields, hiddenFields, fieldRoles } = ··· 991 991 async deleteItemType(_, params, context) { 992 992 const user = context.getUser(); 993 993 if (user == null) { 994 - throw new AuthenticationError('User required.'); 994 + throw unauthenticatedError('User required.'); 995 995 } 996 996 997 997 const { id } = params; ··· 1026 1026 ) => { 1027 1027 const user = context.getUser(); 1028 1028 if (user == null) { 1029 - throw new AuthenticationError('User required.'); 1029 + throw unauthenticatedError('User required.'); 1030 1030 } 1031 1031 if ('name' in itemTypeOrSelector) { 1032 1032 return itemTypeOrSelector;
+5 -6
server/graphql/modules/locationBank.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { type LocationBank as TLocationBank } from '../../models/banks/LocationBankModel.js'; 4 2 import { isCoopErrorOfType } from '../../utils/errors.js'; 5 3 import { ··· 8 6 } from '../generated.js'; 9 7 import { type ResolverMap } from '../resolvers.js'; 10 8 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 9 + import { unauthenticatedError } from '../utils/errors.js'; 11 10 12 11 const typeDefs = /* GraphQL */ ` 13 12 type Query { ··· 132 131 async locationBank(_, { id }, context) { 133 132 const user = context.getUser(); 134 133 if (user == null) { 135 - throw new AuthenticationError('Authenticated user required'); 134 + throw unauthenticatedError('Authenticated user required'); 136 135 } 137 136 138 137 return context.dataSources.locationBankAPI.getGraphQLLocationBankFromId({ ··· 147 146 try { 148 147 const user = context.getUser(); 149 148 if (user == null) { 150 - throw new AuthenticationError('User required.'); 149 + throw unauthenticatedError('User required.'); 151 150 } 152 151 153 152 const bank = await context.dataSources.locationBankAPI.createLocationBank( ··· 170 169 try { 171 170 const user = context.getUser(); 172 171 if (user == null) { 173 - throw new AuthenticationError('User required.'); 172 + throw unauthenticatedError('User required.'); 174 173 } 175 174 176 175 const bank = context.dataSources.locationBankAPI.updateLocationBank( ··· 192 191 async deleteLocationBank(_, params, context) { 193 192 const user = context.getUser(); 194 193 if (user == null) { 195 - throw new AuthenticationError('Authenticated user required'); 194 + throw unauthenticatedError('Authenticated user required'); 196 195 } 197 196 198 197 return context.dataSources.locationBankAPI.deleteLocationBank({
+47 -48
server/graphql/modules/manualReviewTool.ts
··· 1 1 /* eslint-disable max-lines */ 2 - import { AuthenticationError } from 'apollo-server-core'; 3 2 import _ from 'lodash'; 4 3 5 4 import { ··· 41 40 import { formatItemSubmissionForGQL } from '../types.js'; 42 41 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 43 42 import { oneOfInputToTaggedUnion } from '../utils/inputHelpers.js'; 43 + import { forbiddenError, unauthenticatedError } from '../utils/errors.js'; 44 44 45 45 const { omit, sumBy } = _; 46 46 ··· 998 998 }, 999 999 }; 1000 1000 1001 - 1002 1001 const ContentManualReviewJobPayload: GQLContentManualReviewJobPayloadResolvers = 1003 1002 { 1004 1003 async item(it, _, context) { ··· 1668 1667 async explicitlyAssignedReviewers(queue, _, context) { 1669 1668 const user = context.getUser(); 1670 1669 if (user == null) { 1671 - throw new AuthenticationError('User required.'); 1670 + throw unauthenticatedError('User required.'); 1672 1671 } 1673 1672 const { id: userId, orgId } = user; 1674 1673 ··· 1684 1683 async hiddenActionIds(queue, _, context) { 1685 1684 const user = context.getUser(); 1686 1685 if (user == null) { 1687 - throw new AuthenticationError('User required.'); 1686 + throw unauthenticatedError('User required.'); 1688 1687 } 1689 1688 const { orgId } = user; 1690 1689 const { id: queueId } = queue; ··· 1746 1745 async getDecisionCounts(_: unknown, { input }, context) { 1747 1746 const user = context.getUser(); 1748 1747 if (user == null) { 1749 - throw new AuthenticationError('Authenticated user required'); 1748 + throw unauthenticatedError('Authenticated user required'); 1750 1749 } 1751 1750 return context.services.ManualReviewToolService.getDecisionCounts({ 1752 1751 ...input, ··· 1766 1765 async getJobCreationCounts(_: unknown, { input }, context) { 1767 1766 const user = context.getUser(); 1768 1767 if (user == null) { 1769 - throw new AuthenticationError('Authenticated user required'); 1768 + throw unauthenticatedError('Authenticated user required'); 1770 1769 } 1771 1770 const result = 1772 1771 await context.services.ManualReviewToolService.getJobCreationCounts({ ··· 1794 1793 async getResolvedJobCounts(_: unknown, { input }, context) { 1795 1794 const user = context.getUser(); 1796 1795 if (user == null) { 1797 - throw new AuthenticationError('Authenticated user required'); 1796 + throw unauthenticatedError('Authenticated user required'); 1798 1797 } 1799 1798 const result = 1800 1799 await context.services.ManualReviewToolService.getResolvedJobCounts({ ··· 1819 1818 async getSkippedJobCounts(_: unknown, { input }, context) { 1820 1819 const user = context.getUser(); 1821 1820 if (user == null) { 1822 - throw new AuthenticationError('Authenticated user required'); 1821 + throw unauthenticatedError('Authenticated user required'); 1823 1822 } 1824 1823 const result = 1825 1824 await context.services.ManualReviewToolService.getSkippedJobCounts({ ··· 1845 1844 async getResolvedJobsForUser(_: unknown, { timeZone }, context) { 1846 1845 const user = context.getUser(); 1847 1846 if (user == null) { 1848 - throw new AuthenticationError('Authenticated user required'); 1847 + throw unauthenticatedError('Authenticated user required'); 1849 1848 } 1850 1849 1851 1850 const counts = ··· 1871 1870 async getSkippedJobsForUser(_: unknown, { timeZone }, context) { 1872 1871 const user = context.getUser(); 1873 1872 if (user == null) { 1874 - throw new AuthenticationError('Authenticated user required'); 1873 + throw unauthenticatedError('Authenticated user required'); 1875 1874 } 1876 1875 1877 1876 const counts = ··· 1897 1896 async getTimeToAction(_: unknown, { input }, context) { 1898 1897 const user = context.getUser(); 1899 1898 if (user == null) { 1900 - throw new AuthenticationError('Authenticated user required'); 1899 + throw unauthenticatedError('Authenticated user required'); 1901 1900 } 1902 1901 const result = 1903 1902 await context.services.ManualReviewToolService.getDecisionTimeToAction({ ··· 1917 1916 async getTotalPendingJobsCount(_: unknown, __: unknown, context) { 1918 1917 const user = context.getUser(); 1919 1918 if (user == null) { 1920 - throw new AuthenticationError('Authenticated user required'); 1919 + throw unauthenticatedError('Authenticated user required'); 1921 1920 } 1922 1921 const allQueues = 1923 1922 await context.services.ManualReviewToolService.getAllQueuesForOrgAndDangerouslyBypassPermissioning( ··· 1933 1932 async getRecentDecisions(_: unknown, { input }, context) { 1934 1933 const user = context.getUser(); 1935 1934 if (user == null) { 1936 - throw new AuthenticationError('Authenticated user required'); 1935 + throw unauthenticatedError('Authenticated user required'); 1937 1936 } 1938 1937 const permissions = user.getPermissions(); 1939 1938 const { filter, page } = input; ··· 1982 1981 async getDecidedJob(_: unknown, { id }, context) { 1983 1982 const user = context.getUser(); 1984 1983 if (user == null) { 1985 - throw new AuthenticationError('Authenticated user required'); 1984 + throw unauthenticatedError('Authenticated user required'); 1986 1985 } 1987 1986 1988 1987 return context.services.ManualReviewToolService.getDecidedJob({ ··· 1993 1992 async getDecidedJobFromJobId(_: unknown, { id }, context) { 1994 1993 const user = context.getUser(); 1995 1994 if (user == null) { 1996 - throw new AuthenticationError('Authenticated user required'); 1995 + throw unauthenticatedError('Authenticated user required'); 1997 1996 } 1998 1997 1999 1998 return context.services.ManualReviewToolService.getDecidedJobFromJobId({ ··· 2009 2008 ) { 2010 2009 const user = context.getUser(); 2011 2010 if (user == null) { 2012 - throw new AuthenticationError('User required.'); 2011 + throw unauthenticatedError('User required.'); 2013 2012 } 2014 2013 2015 2014 const queue = ··· 2021 2020 async getCommentsForJob(_: unknown, { jobId }, context) { 2022 2021 const user = context.getUser(); 2023 2022 if (user == null) { 2024 - throw new AuthenticationError('User required.'); 2023 + throw unauthenticatedError('User required.'); 2025 2024 } 2026 2025 return context.services.ManualReviewToolService.getJobComments({ 2027 2026 orgId: user.orgId, ··· 2031 2030 async getExistingJobsForItem(_, params, context) { 2032 2031 const user = context.getUser(); 2033 2032 if (user == null) { 2034 - throw new AuthenticationError('Authenticated user required'); 2033 + throw unauthenticatedError('Authenticated user required'); 2035 2034 } 2036 2035 2037 2036 return context.services.ManualReviewToolService.getExistingJobsForItem({ ··· 2043 2042 async getDecisionsTable(_, params, context) { 2044 2043 const user = context.getUser(); 2045 2044 if (user == null) { 2046 - throw new AuthenticationError('Authenticated user required'); 2045 + throw unauthenticatedError('Authenticated user required'); 2047 2046 } 2048 2047 2049 2048 return context.services.ManualReviewToolService.getDecisionCountsTable({ ··· 2060 2059 async getSkipsForRecentDecisions(_, { input }, context) { 2061 2060 const user = context.getUser(); 2062 2061 if (user == null) { 2063 - throw new AuthenticationError('Authenticated user required'); 2062 + throw unauthenticatedError('Authenticated user required'); 2064 2063 } 2065 2064 const filter = input.filter; 2066 2065 ··· 2108 2107 async dequeueManualReviewJob(_, { queueId }, context) { 2109 2108 const user = context.getUser(); 2110 2109 if (user == null) { 2111 - throw new AuthenticationError('User required.'); 2110 + throw unauthenticatedError('User required.'); 2112 2111 } 2113 2112 2114 2113 const { id: userId, orgId } = user; ··· 2135 2134 async submitManualReviewDecision(_, params, context) { 2136 2135 const user = context.getUser(); 2137 2136 if (user == null) { 2138 - throw new AuthenticationError('User required.'); 2137 + throw unauthenticatedError('User required.'); 2139 2138 } 2140 2139 2141 2140 const { id: userId, orgId, email: userEmail } = user; ··· 2237 2236 async createManualReviewQueue(_, params, context) { 2238 2237 const user = context.getUser(); 2239 2238 if (user == null) { 2240 - throw new AuthenticationError('Authenticated user required'); 2239 + throw unauthenticatedError('Authenticated user required'); 2241 2240 } 2242 2241 2243 2242 const { ··· 2279 2278 }, 2280 2279 async updateManualReviewQueue(_, params, context) { 2281 2280 const user = context.getUser(); 2282 - if ( 2283 - user == null || 2284 - !user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES) 2285 - ) { 2286 - throw new AuthenticationError('Authenticated user required'); 2281 + if (user == null) { 2282 + throw unauthenticatedError('Authenticated user required'); 2283 + } 2284 + if (!user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES)) { 2285 + throw forbiddenError('User does not have permission to edit MRT queues'); 2287 2286 } 2288 2287 2289 2288 const { ··· 2323 2322 }, 2324 2323 async deleteManualReviewQueue(_, params, context) { 2325 2324 const user = context.getUser(); 2326 - if ( 2327 - user == null || 2328 - !user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES) 2329 - ) { 2330 - throw new AuthenticationError('Authenticated user required'); 2325 + if (user == null) { 2326 + throw unauthenticatedError('Authenticated user required'); 2327 + } 2328 + if (!user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES)) { 2329 + throw forbiddenError('User does not have permission to edit MRT queues'); 2331 2330 } 2332 2331 2333 2332 return context.services.ManualReviewToolService.deleteManualReviewQueue( ··· 2337 2336 }, 2338 2337 async addAccessibleQueuesToUser(_, params, context) { 2339 2338 const user = context.getUser(); 2340 - if ( 2341 - user == null || 2342 - !user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES) 2343 - ) { 2344 - throw new AuthenticationError('Authenticated user required'); 2339 + if (user == null) { 2340 + throw unauthenticatedError('Authenticated user required'); 2341 + } 2342 + if (!user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES)) { 2343 + throw forbiddenError('User does not have permission to edit MRT queues'); 2345 2344 } 2346 2345 2347 2346 await context.services.ManualReviewToolService.addAccessibleQueuesForUser( ··· 2357 2356 }, 2358 2357 async removeAccessibleQueuesToUser(_, params, context) { 2359 2358 const user = context.getUser(); 2360 - if ( 2361 - user == null || 2362 - !user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES) 2363 - ) { 2364 - throw new AuthenticationError('Authenticated user required'); 2359 + if (user == null) { 2360 + throw unauthenticatedError('Authenticated user required'); 2361 + } 2362 + if (!user.getPermissions().includes(UserPermission.EDIT_MRT_QUEUES)) { 2363 + throw forbiddenError('User does not have permission to edit MRT queues'); 2365 2364 } 2366 2365 2367 2366 await context.services.ManualReviewToolService.removeAccessibleQueuesForUser( ··· 2381 2380 // that are being deleted 2382 2381 const user = context.getUser(); 2383 2382 if (user == null) { 2384 - throw new AuthenticationError('Authenticated user required'); 2383 + throw unauthenticatedError('Authenticated user required'); 2385 2384 } 2386 2385 try { 2387 2386 await context.services.ManualReviewToolService.deleteAllJobsFromQueue({ ··· 2404 2403 async createManualReviewJobComment(_, params, context) { 2405 2404 const user = context.getUser(); 2406 2405 if (user == null) { 2407 - throw new AuthenticationError('Authenticated user required'); 2406 + throw unauthenticatedError('Authenticated user required'); 2408 2407 } 2409 2408 2410 2409 try { ··· 2432 2431 async deleteManualReviewJobComment(_, params, context) { 2433 2432 const user = context.getUser(); 2434 2433 if (user == null) { 2435 - throw new AuthenticationError('Authenticated user required'); 2434 + throw unauthenticatedError('Authenticated user required'); 2436 2435 } 2437 2436 2438 2437 const { id: userId, orgId } = user; ··· 2452 2451 async logSkip(_, { input }, context) { 2453 2452 const user = context.getUser(); 2454 2453 if (user == null) { 2455 - throw new AuthenticationError('Authenticated user required'); 2454 + throw unauthenticatedError('Authenticated user required'); 2456 2455 } 2457 2456 try { 2458 2457 await context.services.ManualReviewToolService.logSkip({ ··· 2469 2468 async releaseJobLock(_, { input }, context) { 2470 2469 const user = context.getUser(); 2471 2470 if (user == null) { 2472 - throw new AuthenticationError('Authenticated user required'); 2471 + throw unauthenticatedError('Authenticated user required'); 2473 2472 } 2474 2473 try { 2475 2474 await context.services.ManualReviewToolService.releaseJobLock({
+6 -10
server/graphql/modules/ncmec.ts
··· 1 - import { AuthenticationError } from 'apollo-server-core'; 2 - import { UserInputError } from 'apollo-server-express'; 3 - 4 1 import { formatItemSubmissionForGQL } from '../../graphql/types.js'; 5 2 import type { 6 3 GQLMutationResolvers, 7 4 GQLNcmecOrgSettings, 8 5 GQLQueryResolvers, 9 6 } from '../generated.js'; 7 + import { unauthenticatedError, userInputError } from '../utils/errors.js'; 10 8 11 9 /** Input shape for updateNcmecOrgSettings; matches NcmecOrgSettingsInput in schema (used so resolver type-checks even if generated types are stale). */ 12 10 type NcmecOrgSettingsInputShape = { ··· 215 213 async ncmecReportById(_, { reportId }, context) { 216 214 const user = context.getUser(); 217 215 if (!user) { 218 - throw new AuthenticationError('User required.'); 216 + throw unauthenticatedError('User required.'); 219 217 } 220 218 const report = await context.services.NcmecService.getNcmecReportById({ 221 219 orgId: user.orgId, ··· 248 246 async ncmecThreads(_, { userId, reportedMessages }, context) { 249 247 const user = context.getUser(); 250 248 if (!user) { 251 - throw new AuthenticationError('User required.'); 249 + throw unauthenticatedError('User required.'); 252 250 } 253 251 const threads = await context.services.NcmecService.getNcmecMessages( 254 252 user.orgId, ··· 267 265 async ncmecOrgSettings(_, __, context): Promise<GQLNcmecOrgSettings | null> { 268 266 const user = context.getUser(); 269 267 if (!user) { 270 - throw new AuthenticationError('User required.'); 268 + throw unauthenticatedError('User required.'); 271 269 } 272 270 const settings = await context.services.NcmecService.getNcmecOrgSettings( 273 271 user.orgId, ··· 280 278 async updateNcmecOrgSettings(_, { input: rawInput }, context) { 281 279 const user = context.getUser(); 282 280 if (!user) { 283 - throw new AuthenticationError('User required.'); 281 + throw unauthenticatedError('User required.'); 284 282 } 285 283 const input = rawInput as NcmecOrgSettingsInputShape; 286 284 const defaultInternetDetailType = ··· 292 290 trimmed !== '' && 293 291 !VALID_NCMEC_INTERNET_DETAIL_TYPES.includes(trimmed) 294 292 ) { 295 - throw new UserInputError( 296 - `defaultInternetDetailType must be one of: ${VALID_NCMEC_INTERNET_DETAIL_TYPES.join(', ')}`, 297 - ); 293 + throw userInputError(`defaultInternetDetailType must be one of: ${VALID_NCMEC_INTERNET_DETAIL_TYPES.join(', ')}`); 298 294 } 299 295 return trimmed === '' ? null : trimmed; 300 296 })();
+42 -49
server/graphql/modules/org.ts
··· 1 1 /* eslint-disable max-lines */ 2 - import { AuthenticationError } from 'apollo-server-express'; 3 2 4 3 import { isCoopErrorOfType } from '../../utils/errors.js'; 5 4 import { __throw } from '../../utils/misc.js'; ··· 11 10 type GQLPendingInvite, 12 11 type GQLQueryResolvers, 13 12 } from '../generated.js'; 13 + import { GraphQLError } from 'graphql'; 14 14 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 15 + import { forbiddenError, unauthenticatedError } from '../utils/errors.js'; 15 16 16 17 const typeDefs = /* GraphQL */ ` 17 18 type Org { ··· 172 173 async org(_, { id }, context) { 173 174 const user = context.getUser(); 174 175 if (user == null || user.orgId !== id) { 175 - throw new AuthenticationError('Authenticated user required'); 176 + throw unauthenticatedError('Authenticated user required'); 176 177 } 177 178 178 179 return context.dataSources.orgAPI.getGraphQLOrgFromId(id); ··· 186 187 async appealSettings(_, __, context) { 187 188 const user = context.getUser(); 188 189 if (user == null || !user.orgId) { 189 - throw new AuthenticationError('Authenticated user required'); 190 + throw unauthenticatedError('Authenticated user required'); 190 191 } 191 192 const settings = 192 193 await context.services.OrgSettingsService.getAppealSettings(user.orgId); ··· 202 203 async actions(org, _, context) { 203 204 const user = context.getUser(); 204 205 if (!user || user.orgId !== org.id) { 205 - throw new AuthenticationError('User required.'); 206 + throw unauthenticatedError('User required.'); 206 207 } 207 208 208 209 return org.getActions(); ··· 210 211 async contentTypes(org, _, context) { 211 212 const user = context.getUser(); 212 213 if (!user || user.orgId !== org.id) { 213 - throw new AuthenticationError('User required.'); 214 + throw unauthenticatedError('User required.'); 214 215 } 215 216 return org.getContentTypes(); 216 217 }, 217 218 async itemTypes(org, _, context) { 218 219 const user = context.getUser(); 219 220 if (!user || user.orgId !== org.id) { 220 - throw new AuthenticationError('User required.'); 221 + throw unauthenticatedError('User required.'); 221 222 } 222 223 return context.services.ModerationConfigService.getItemTypes({ 223 224 orgId: org.id, ··· 226 227 async users(org, _, context) { 227 228 const user = context.getUser(); 228 229 if (!user || user.orgId !== org.id) { 229 - throw new AuthenticationError('User required.'); 230 + throw unauthenticatedError('User required.'); 230 231 } 231 232 return org.getUsers(); 232 233 }, 233 234 async pendingInvites(org, _, context): Promise<GQLPendingInvite[]> { 234 235 const user = context.getUser(); 235 236 if (!user || user.orgId !== org.id) { 236 - throw new AuthenticationError('User required.'); 237 + throw unauthenticatedError('User required.'); 237 238 } 238 239 239 240 if (!user.getPermissions().includes('MANAGE_ORG')) { 240 - throw new AuthenticationError( 241 - 'User does not have permission to view pending invites', 242 - ); 241 + throw forbiddenError('User does not have permission to view pending invites'); 243 242 } 244 243 245 244 const invites = ··· 249 248 async rules(org, _, context) { 250 249 const user = context.getUser(); 251 250 if (!user || user.orgId !== org.id) { 252 - throw new AuthenticationError('User required.'); 251 + throw unauthenticatedError('User required.'); 253 252 } 254 253 return org.getRules(); 255 254 }, 256 255 async routingRules(org, _, context) { 257 256 const user = context.getUser(); 258 257 if (!user || user.orgId !== org.id) { 259 - throw new AuthenticationError('User required'); 258 + throw unauthenticatedError('User required'); 260 259 } 261 260 262 261 return context.services.ManualReviewToolService.getRoutingRules({ ··· 269 268 async appealsRoutingRules(org, _, context) { 270 269 const user = context.getUser(); 271 270 if (!user || user.orgId !== org.id) { 272 - throw new AuthenticationError('User required'); 271 + throw unauthenticatedError('User required'); 273 272 } 274 273 275 274 return context.services.ManualReviewToolService.getAppealsRoutingRules({ ··· 282 281 async reportingRules(org, _, context) { 283 282 const user = context.getUser(); 284 283 if (!user || user.orgId !== org.id) { 285 - throw new AuthenticationError('User required'); 284 + throw unauthenticatedError('User required'); 286 285 } 287 286 288 287 return context.services.ReportingService.getReportingRules({ ··· 295 294 async mrtQueues(org, _, context) { 296 295 const user = context.getUser(); 297 296 if (!user || user.orgId !== org.id) { 298 - throw new AuthenticationError('User required'); 297 + throw unauthenticatedError('User required'); 299 298 } 300 299 return context.services.ManualReviewToolService.getAllQueuesForOrgAndDangerouslyBypassPermissioning( 301 300 { ··· 306 305 async apiKey(org, _, context) { 307 306 const user = context.getUser(); 308 307 if (!user || user.orgId !== org.id) { 309 - throw new AuthenticationError('User required.'); 308 + throw unauthenticatedError('User required.'); 310 309 } 311 310 const apiKey = await context.dataSources.orgAPI.getActivatedApiKeyForOrg( 312 311 org.id, ··· 317 316 if (!apiKey) { 318 317 return process.env.NODE_ENV !== 'production' 319 318 ? '' 320 - : __throw(new AuthenticationError('API Key not found')); 319 + : __throw(new GraphQLError('API Key not found')); 321 320 } 322 321 323 322 return apiKey.key; ··· 325 324 async integrationConfigs(org, _, context) { 326 325 const user = context.getUser(); 327 326 if (!user || user.orgId !== org.id) { 328 - throw new AuthenticationError('User required.'); 327 + throw unauthenticatedError('User required.'); 329 328 } 330 329 331 330 return context.dataSources.integrationAPI.getAllIntegrationConfigs( ··· 336 335 async signals(org, { customOnly }, context) { 337 336 const user = context.getUser(); 338 337 if (!user || user.orgId !== org.id) { 339 - throw new AuthenticationError('User required.'); 338 + throw unauthenticatedError('User required.'); 340 339 } 341 340 342 341 return customOnly ··· 346 345 async userStrikeThresholds(org, _, context) { 347 346 const user = context.getUser(); 348 347 if (!user || user.orgId !== org.id) { 349 - throw new AuthenticationError('User required.'); 348 + throw unauthenticatedError('User required.'); 350 349 } 351 350 352 351 return context.services.ModerationConfigService.getUserStrikeThresholdsForOrg( ··· 356 355 async policies(org, _, context) { 357 356 const user = context.getUser(); 358 357 if (!user || user.orgId !== org.id) { 359 - throw new AuthenticationError('User required.'); 358 + throw unauthenticatedError('User required.'); 360 359 } 361 360 362 361 return context.services.ModerationConfigService.getPolicies({ ··· 367 366 async banks(org, _, context) { 368 367 const user = context.getUser(); 369 368 if (!user || user.orgId !== org.id) { 370 - throw new AuthenticationError('User required.'); 369 + throw unauthenticatedError('User required.'); 371 370 } 372 371 return org; 373 372 }, 374 373 async hasReportingRulesEnabled(org, _, context) { 375 374 const user = context.getUser(); 376 375 if (!user || user.orgId !== org.id) { 377 - throw new AuthenticationError('User required.'); 376 + throw unauthenticatedError('User required.'); 378 377 } 379 378 return context.services.OrgSettingsService.hasReportingRulesEnabled(org.id); 380 379 }, 381 380 async hasAppealsEnabled(org, _, context) { 382 381 const user = context.getUser(); 383 382 if (!user || user.orgId !== org.id) { 384 - throw new AuthenticationError('User required.'); 383 + throw unauthenticatedError('User required.'); 385 384 } 386 385 return context.services.OrgSettingsService.hasAppealsEnabled(org.id); 387 386 }, 388 387 async userStrikeTTL(org, _, context) { 389 388 const user = context.getUser(); 390 389 if (!user || user.orgId !== org.id) { 391 - throw new AuthenticationError('User required.'); 390 + throw unauthenticatedError('User required.'); 392 391 } 393 392 return context.services.OrgSettingsService.userStrikeTTLInDays(org.id); 394 393 }, 395 394 async publicSigningKey(org, _, context) { 396 395 const user = context.getUser(); 397 396 if (!user || user.orgId !== org.id) { 398 - throw new AuthenticationError('User required.'); 397 + throw unauthenticatedError('User required.'); 399 398 } 400 399 return context.dataSources.orgAPI.getPublicSigningKeyPem(org.id); 401 400 }, 402 401 async hasNCMECReportingEnabled(org, _, context) { 403 402 const user = context.getUser(); 404 403 if (!user || user.orgId !== org.id) { 405 - throw new AuthenticationError('User required.'); 404 + throw unauthenticatedError('User required.'); 406 405 } 407 406 return context.services.NcmecService.hasNCMECReportingEnabled(org.id); 408 407 }, 409 408 async ncmecReports(org, _, context) { 410 409 const user = context.getUser(); 411 410 if (!user || user.orgId !== org.id) { 412 - throw new AuthenticationError('User required.'); 411 + throw unauthenticatedError('User required.'); 413 412 } 414 413 const reports = await context.services.NcmecService.getNcmecReports({ 415 414 orgId: user.orgId, ··· 489 488 async ssoUrl(org, _, context) { 490 489 const user = context.getUser(); 491 490 if (user == null || user.orgId !== org.id) { 492 - throw new AuthenticationError('Authenticated user required'); 491 + throw unauthenticatedError('Authenticated user required'); 493 492 } 494 493 495 494 if (!user.getPermissions().includes('MANAGE_ORG')) { 496 - throw new AuthenticationError( 497 - 'User does not have permission to manage SSO settings', 498 - ); 495 + throw forbiddenError('User does not have permission to manage SSO settings'); 499 496 } 500 497 501 498 const settings = await context.services.OrgSettingsService.getSamlSettings( ··· 511 508 async ssoCert(org, _, context) { 512 509 const user = context.getUser(); 513 510 if (user == null || user.orgId !== org.id) { 514 - throw new AuthenticationError('Authenticated user required'); 511 + throw unauthenticatedError('Authenticated user required'); 515 512 } 516 513 517 514 if (!user.getPermissions().includes('MANAGE_ORG')) { 518 - throw new AuthenticationError( 519 - 'User does not have permission to manage SSO settings', 520 - ); 515 + throw forbiddenError('User does not have permission to manage SSO settings'); 521 516 } 522 517 523 518 const settings = await context.services.OrgSettingsService.getSamlSettings( ··· 543 538 async textBanks(org, _, context) { 544 539 const user = context.getUser(); 545 540 if (!user || user.orgId !== org.id) { 546 - throw new AuthenticationError('User required.'); 541 + throw unauthenticatedError('User required.'); 547 542 } 548 543 return context.services.ModerationConfigService.getTextBanks({ 549 544 orgId: org.id, ··· 552 547 async locationBanks(org, _, context) { 553 548 const user = context.getUser(); 554 549 if (!user || user.orgId !== org.id) { 555 - throw new AuthenticationError('User required.'); 550 + throw unauthenticatedError('User required.'); 556 551 } 557 552 return context.dataSources.locationBankAPI.getGraphQLLocationBanksForOrg( 558 553 org.id, ··· 561 556 async hashBanks(org, _, context) { 562 557 const user = context.getUser(); 563 558 if (!user || user.orgId !== org.id) { 564 - throw new AuthenticationError('User required.'); 559 + throw unauthenticatedError('User required.'); 565 560 } 566 561 return context.services.HMAHashBankService.listBanks(org.id); 567 562 }, ··· 588 583 async updateAppealSettings(_, { input }, context) { 589 584 const user = context.getUser(); 590 585 if (!user || !user.orgId) { 591 - throw new AuthenticationError('User required.'); 586 + throw unauthenticatedError('User required.'); 592 587 } 593 588 const settings = 594 589 await context.services.OrgSettingsService.updateAppealSettings({ ··· 603 598 async setOrgDefaultSafetySettings(_, params, context) { 604 599 const user = context.getUser(); 605 600 if (!user) { 606 - throw new AuthenticationError('User required.'); 601 + throw unauthenticatedError('User required.'); 607 602 } 608 603 await context.services.UserManagementService.upsertOrgDefaultUserInterfaceSettings( 609 604 { ··· 617 612 async setAllUserStrikeThresholds(_, params, context) { 618 613 const user = context.getUser(); 619 614 if (!user) { 620 - throw new AuthenticationError('User required.'); 615 + throw unauthenticatedError('User required.'); 621 616 } 622 617 623 618 await context.services.ModerationConfigService.setAllUserStrikeThresholds({ ··· 630 625 async updateUserStrikeTTL(_, { input }, context) { 631 626 const user = context.getUser(); 632 627 if (!user) { 633 - throw new AuthenticationError('User required.'); 628 + throw unauthenticatedError('User required.'); 634 629 } 635 630 await context.services.OrgSettingsService.updateUserStrikeTTL({ 636 631 orgId: user.orgId, ··· 641 636 async updateSSOCredentials(_, { input }, context) { 642 637 const user = context.getUser(); 643 638 if (!user) { 644 - throw new AuthenticationError('User required.'); 639 + throw unauthenticatedError('User required.'); 645 640 } 646 641 647 642 return context.services.OrgSettingsService.updateSamlSettings({ ··· 653 648 async updateOrgInfo(_, { input }, context) { 654 649 const user = context.getUser(); 655 650 if (!user) { 656 - throw new AuthenticationError('User required.'); 651 + throw unauthenticatedError('User required.'); 657 652 } 658 653 659 654 if (!user.getPermissions().includes('MANAGE_ORG')) { 660 - throw new AuthenticationError( 661 - 'User does not have permission to manage org info', 662 - ); 655 + throw forbiddenError('User does not have permission to manage org info'); 663 656 } 664 657 665 658 await context.dataSources.orgAPI.updateOrgInfo(user.orgId, input);
+5 -5
server/graphql/modules/policy.ts
··· 1 - import { AuthenticationError } from 'apollo-server-core'; 2 1 import _ from 'lodash'; 3 2 4 3 import { type Policy } from '../../models/PolicyModel.js'; ··· 11 10 type GQLUpdatePolicyResponseResolvers, 12 11 } from '../generated.js'; 13 12 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 13 + import { unauthenticatedError } from '../utils/errors.js'; 14 14 15 15 const { partition } = _; 16 16 ··· 101 101 async policy(_: unknown, { id }: GQLQueryPolicyArgs, context) { 102 102 const user = context.getUser(); 103 103 if (user == null) { 104 - throw new AuthenticationError('Authenticated user required'); 104 + throw unauthenticatedError('Authenticated user required'); 105 105 } 106 106 107 107 return context.services.ModerationConfigService.getPolicy({ ··· 115 115 async addPolicies(_: unknown, params, context) { 116 116 const user = context.getUser(); 117 117 if (user == null) { 118 - throw new AuthenticationError('Authenticated user required.'); 118 + throw unauthenticatedError('Authenticated user required.'); 119 119 } 120 120 121 121 const { policies } = params; ··· 160 160 async updatePolicy(_: unknown, { input }, context) { 161 161 const user = context.getUser(); 162 162 if (user == null) { 163 - throw new AuthenticationError('Authenticated user required.'); 163 + throw unauthenticatedError('Authenticated user required.'); 164 164 } 165 165 try { 166 166 const { ··· 206 206 async deletePolicy(_: unknown, params: GQLMutationDeletePolicyArgs, context) { 207 207 const user = context.getUser(); 208 208 if (user == null) { 209 - throw new AuthenticationError('Authenticated user required'); 209 + throw unauthenticatedError('Authenticated user required'); 210 210 } 211 211 212 212 return context.services.ModerationConfigService.deletePolicy({
+10 -11
server/graphql/modules/reportingRule.ts
··· 1 - import { AuthenticationError } from 'apollo-server-core'; 2 - 3 1 import { isCoopErrorOfType } from '../../utils/errors.js'; 4 2 import { 5 3 isNonEmptyArray, ··· 14 12 type GQLReportingRuleResolvers, 15 13 } from '../generated.js'; 16 14 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 15 + import { unauthenticatedError } from '../utils/errors.js'; 17 16 18 17 const typeDefs = /* GraphQL */ ` 19 18 enum ReportingRuleStatus { ··· 99 98 async creator(reportingRule, _, { dataSources, getUser }) { 100 99 const user = getUser(); 101 100 if (!user || user.orgId !== reportingRule.orgId) { 102 - throw new AuthenticationError('User required'); 101 + throw unauthenticatedError('User required'); 103 102 } 104 103 if (!reportingRule.creatorId) { 105 104 return null; ··· 114 113 async itemTypes(reportingRule, _, { services, getUser }) { 115 114 const user = getUser(); 116 115 if (!user || user.orgId !== reportingRule.orgId) { 117 - throw new AuthenticationError('User required'); 116 + throw unauthenticatedError('User required'); 118 117 } 119 118 120 119 const itemTypes = await services.ModerationConfigService.getItemTypes({ ··· 128 127 async actions(reportingRule, _, { dataSources, getUser }) { 129 128 const user = getUser(); 130 129 if (user == null) { 131 - throw new AuthenticationError('Authenticated user required'); 130 + throw unauthenticatedError('Authenticated user required'); 132 131 } 133 132 134 133 return dataSources.actionAPI.getGraphQLActionsFromIds( ··· 139 138 async policies(reportingRule, _, { services, getUser }) { 140 139 const user = getUser(); 141 140 if (user == null) { 142 - throw new AuthenticationError('Authenticated user required'); 141 + throw unauthenticatedError('Authenticated user required'); 143 142 } 144 143 const { orgId } = user; 145 144 ··· 159 158 // insights resolver. But verify the rule is owned by the user's org 160 159 const user = context.getUser(); 161 160 if (user == null) { 162 - throw new AuthenticationError('User required'); 161 + throw unauthenticatedError('User required'); 163 162 } 164 163 165 164 if (rule.orgId !== user.orgId) { ··· 174 173 async reportingRule(_, { id }, { services, getUser }) { 175 174 const user = getUser(); 176 175 if (user == null) { 177 - throw new AuthenticationError('Authenticated user required'); 176 + throw unauthenticatedError('Authenticated user required'); 178 177 } 179 178 180 179 const reportingRules = await services.ReportingService.getReportingRules({ ··· 189 188 async createReportingRule(_, { input }, { services, getUser }) { 190 189 const user = getUser(); 191 190 if (user == null) { 192 - throw new AuthenticationError('Authenticated user required'); 191 + throw unauthenticatedError('Authenticated user required'); 193 192 } 194 193 195 194 const { itemTypeIds, actionIds } = input; ··· 228 227 async updateReportingRule(_, { input }, { services, getUser }) { 229 228 const user = getUser(); 230 229 if (user == null) { 231 - throw new AuthenticationError('Authenticated user required'); 230 + throw unauthenticatedError('Authenticated user required'); 232 231 } 233 232 const { id, name, description, status, conditionSet, policyIds } = input; 234 233 ··· 283 282 async deleteReportingRule(_, { id }, { services, getUser }) { 284 283 const user = getUser(); 285 284 if (user == null) { 286 - throw new AuthenticationError('Authenticated user required'); 285 + throw unauthenticatedError('Authenticated user required'); 287 286 } 288 287 289 288 return services.ReportingService.deleteReportingRule({
+4 -5
server/graphql/modules/retroaction.ts
··· 1 - import { AuthenticationError, ForbiddenError } from 'apollo-server-express'; 2 - 3 1 import { 4 2 hasPermission, 5 3 UserPermission, 6 4 } from '../../models/types/permissioning.js'; 7 5 import { type GQLMutationRunRetroactionArgs } from '../generated.js'; 8 6 import { type Context } from '../resolvers.js'; 7 + import { forbiddenError, unauthenticatedError } from '../utils/errors.js'; 9 8 10 9 const typeDefs = /* GraphQL */ ` 11 10 type Mutation { ··· 46 45 ); 47 46 48 47 if (user == null) { 49 - throw new AuthenticationError('Authenticated user required'); 48 + throw unauthenticatedError('Authenticated user required'); 50 49 } else if (!hasPermission(UserPermission.RUN_RETROACTION, user.role)) { 51 - throw new ForbiddenError('User not authorized to create backtests.'); 50 + throw forbiddenError('User not authorized to create backtests.'); 52 51 } else if (!rule || user.orgId !== rule.orgId) { 53 - throw new ForbiddenError('Invalid rule.'); 52 + throw forbiddenError('Invalid rule.'); 54 53 } 55 54 56 55 return context.dataSources.ruleAPI.runRetroaction(params.input, user);
+7 -8
server/graphql/modules/routingRule.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { 4 2 hasPermission, 5 3 UserPermission, ··· 18 16 type GQLRoutingRuleResolvers, 19 17 } from '../generated.js'; 20 18 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 19 + import { unauthenticatedError } from '../utils/errors.js'; 21 20 22 21 const typeDefs = /* GraphQL */ ` 23 22 type RoutingRule { ··· 125 124 async destinationQueue(routingRule, _, context) { 126 125 const user = context.getUser(); 127 126 if (!user || user.orgId !== routingRule.orgId) { 128 - throw new AuthenticationError('User required'); 127 + throw unauthenticatedError('User required'); 129 128 } 130 129 131 130 const userCanEditMRTQueues = hasPermission( ··· 155 154 async itemTypes(routingRule, _, context) { 156 155 const user = context.getUser(); 157 156 if (!user || user.orgId !== routingRule.orgId) { 158 - throw new AuthenticationError('User required'); 157 + throw unauthenticatedError('User required'); 159 158 } 160 159 161 160 const itemTypes = ··· 177 176 const { itemTypeIds } = params.input; 178 177 179 178 if (user == null) { 180 - throw new AuthenticationError('User required.'); 179 + throw unauthenticatedError('User required.'); 181 180 } 182 181 183 182 if (!itemTypeIdsAreValid(itemTypeIds)) { ··· 216 215 const user = context.getUser(); 217 216 const { itemTypeIds } = params.input; 218 217 if (user == null) { 219 - throw new AuthenticationError('User required.'); 218 + throw unauthenticatedError('User required.'); 220 219 } 221 220 222 221 if (itemTypeIds && !itemTypeIdsAreValid(itemTypeIds)) { ··· 261 260 async deleteRoutingRule(_, params, context) { 262 261 const user = context.getUser(); 263 262 if (user == null) { 264 - throw new AuthenticationError('User required.'); 263 + throw unauthenticatedError('User required.'); 265 264 } 266 265 267 266 return context.services.ManualReviewToolService.deleteRoutingRule({ ··· 272 271 async reorderRoutingRules(_, params, context) { 273 272 const user = context.getUser(); 274 273 if (user == null) { 275 - throw new AuthenticationError('User required.'); 274 + throw unauthenticatedError('User required.'); 276 275 } 277 276 278 277 const { order } = params.input;
+19 -21
server/graphql/modules/rule.ts
··· 1 1 /* eslint-disable max-lines */ 2 - import { AuthenticationError } from 'apollo-server-express'; 3 2 4 3 import { isConditionSet } from '../../condition_evaluator/condition.js'; 5 4 import { ··· 28 27 } from '../generated.js'; 29 28 import { type Context, type ResolverMap } from '../resolvers.js'; 30 29 import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; 30 + import { unauthenticatedError } from '../utils/errors.js'; 31 31 32 32 const typeDefs = /* GraphQL */ ` 33 33 type Query { ··· 437 437 type: AggregationType! 438 438 } 439 439 440 - 441 - 442 440 type RuleHasRunningBacktestsError implements Error { 443 441 title: String! 444 442 status: Int! ··· 481 479 async rule(_, { id }, { dataSources, getUser }) { 482 480 const user = await getUser(); 483 481 if (user == null) { 484 - throw new AuthenticationError('Authenticated user required'); 482 + throw unauthenticatedError('Authenticated user required'); 485 483 } 486 484 487 485 return dataSources.ruleAPI.getGraphQLRuleFromId(id, user.orgId); ··· 492 490 async createContentRule(_, params, context) { 493 491 const user = context.getUser(); 494 492 if (user == null) { 495 - throw new AuthenticationError('Authenticated user required'); 493 + throw unauthenticatedError('Authenticated user required'); 496 494 } 497 495 498 496 try { ··· 518 516 try { 519 517 const user = context.getUser(); 520 518 if (user == null) { 521 - throw new AuthenticationError('Authenticated user required'); 519 + throw unauthenticatedError('Authenticated user required'); 522 520 } 523 521 524 522 const rule = await context.dataSources.ruleAPI.updateContentRule({ ··· 548 546 async createUserRule(_, params, context) { 549 547 const user = context.getUser(); 550 548 if (user == null) { 551 - throw new AuthenticationError('Authenticated user required'); 549 + throw unauthenticatedError('Authenticated user required'); 552 550 } 553 551 554 552 try { ··· 570 568 async updateUserRule(_, params, context) { 571 569 const user = context.getUser(); 572 570 if (user == null) { 573 - throw new AuthenticationError('Authenticated user required'); 571 + throw unauthenticatedError('Authenticated user required'); 574 572 } 575 573 576 574 try { ··· 598 596 async deleteRule(_, params, context) { 599 597 const user = context.getUser(); 600 598 if (user == null) { 601 - throw new AuthenticationError('Authenticated user required'); 599 + throw unauthenticatedError('Authenticated user required'); 602 600 } 603 601 604 602 return context.dataSources.ruleAPI.deleteRule({ ··· 612 610 async name(conditionInputField, _, context) { 613 611 const user = context.getUser(); 614 612 if (user == null) { 615 - throw new AuthenticationError('Authenticated user required'); 613 + throw unauthenticatedError('Authenticated user required'); 616 614 } 617 615 618 616 if (conditionInputField.type !== 'CONTENT_DERIVED_FIELD') { ··· 640 638 async creator(rule, _, context) { 641 639 const user = context.getUser(); 642 640 if (user == null) { 643 - throw new AuthenticationError('Authenticated user required'); 641 + throw unauthenticatedError('Authenticated user required'); 644 642 } 645 643 646 644 return rule.getCreator(); ··· 648 646 async itemTypes(rule, _, { services, getUser }) { 649 647 const user = getUser(); 650 648 if (user == null) { 651 - throw new AuthenticationError('Authenticated user required'); 649 + throw unauthenticatedError('Authenticated user required'); 652 650 } 653 651 return services.ModerationConfigService.getItemTypesForRule({ 654 652 orgId: user.orgId, ··· 658 656 async actions(rule, _, context) { 659 657 const user = context.getUser(); 660 658 if (user == null) { 661 - throw new AuthenticationError('Authenticated user required'); 659 + throw unauthenticatedError('Authenticated user required'); 662 660 } 663 661 664 662 return rule.getActions(); ··· 666 664 async policies(rule, _, context) { 667 665 const user = context.getUser(); 668 666 if (user == null) { 669 - throw new AuthenticationError('Authenticated user required'); 667 + throw unauthenticatedError('Authenticated user required'); 670 668 } 671 669 672 670 return rule.getPolicies(); ··· 674 672 async backtests(rule, args, context) { 675 673 const user = context.getUser(); 676 674 if (user == null) { 677 - throw new AuthenticationError('Authenticated user required'); 675 + throw unauthenticatedError('Authenticated user required'); 678 676 } 679 677 680 678 const { ids } = args; ··· 685 683 // insights resolver. But verify the rule is owned by the user's org 686 684 const user = context.getUser(); 687 685 if (user == null) { 688 - throw new AuthenticationError('User required'); 686 + throw unauthenticatedError('User required'); 689 687 } 690 688 691 689 if (rule.orgId !== user.orgId) { ··· 700 698 async creator(rule, _, context) { 701 699 const user = context.getUser(); 702 700 if (user == null) { 703 - throw new AuthenticationError('Authenticated user required'); 701 + throw unauthenticatedError('Authenticated user required'); 704 702 } 705 703 706 704 return rule.getCreator(); ··· 708 706 async actions(rule, _, context) { 709 707 const user = context.getUser(); 710 708 if (user == null) { 711 - throw new AuthenticationError('Authenticated user required'); 709 + throw unauthenticatedError('Authenticated user required'); 712 710 } 713 711 714 712 return rule.getActions(); ··· 716 714 async policies(rule, _, context) { 717 715 const user = context.getUser(); 718 716 if (user == null) { 719 - throw new AuthenticationError('Authenticated user required'); 717 + throw unauthenticatedError('Authenticated user required'); 720 718 } 721 719 722 720 return rule.getPolicies(); ··· 724 722 async backtests(rule, args, context) { 725 723 const user = context.getUser(); 726 724 if (user == null) { 727 - throw new AuthenticationError('Authenticated user required'); 725 + throw unauthenticatedError('Authenticated user required'); 728 726 } 729 727 730 728 const { ids } = args; ··· 735 733 // insights resolver. But verify the rule is owned by the user's org 736 734 const user = context.getUser(); 737 735 if (user == null) { 738 - throw new AuthenticationError('Authenticated user required'); 736 + throw unauthenticatedError('Authenticated user required'); 739 737 } 740 738 741 739 if (rule.orgId !== user.orgId) {
+3 -4
server/graphql/modules/signal.ts
··· 1 1 import { type SignalSubcategory } from '@roostorg/types'; 2 - import { AuthenticationError } from 'apollo-server-express'; 2 + 3 3 import { type ReadonlyDeep } from 'type-fest'; 4 4 5 5 import { getIntegrationRegistry } from '../../services/integrationRegistry/index.js'; ··· 16 16 type GQLSupportedLanguagesResolvers, 17 17 } from '../generated.js'; 18 18 import { type ResolverMap } from '../resolvers.js'; 19 + import { unauthenticatedError } from '../utils/errors.js'; 19 20 20 21 const typeDefs = /* GraphQL */ ` 21 22 enum SignalPricingStructureType { ··· 228 229 const user = context.getUser(); 229 230 230 231 if (!user) { 231 - throw new AuthenticationError( 232 - 'User required to load signal disabledInfo.', 233 - ); 232 + throw unauthenticatedError('User required to load signal disabledInfo.'); 234 233 } 235 234 236 235 // Non-null assertion below is safe because, if this resolver ran, it means
+2 -2
server/graphql/modules/spotTest.ts
··· 1 - import { AuthenticationError } from 'apollo-server-core'; 2 1 import { uid } from 'uid'; 3 2 4 3 import { RuleEnvironment } from '../../rule_engine/RuleEngine.js'; 5 4 import { rawItemSubmissionToItemSubmission } from '../../services/itemProcessingService/index.js'; 6 5 import { jsonStringify } from '../../utils/encoding.js'; 7 6 import type { GQLQueryResolvers } from '../generated.js'; 7 + import { unauthenticatedError } from '../utils/errors.js'; 8 8 9 9 const typeDefs = /* GraphQL */ ` 10 10 extend type Query { ··· 21 21 async spotTestRule(_, { ruleId, item }, { services, dataSources, getUser }) { 22 22 const user = getUser(); 23 23 if (user == null) { 24 - throw new AuthenticationError('Authenticated user required'); 24 + throw unauthenticatedError('Authenticated user required'); 25 25 } 26 26 27 27 const [itemTypes, rule] = await Promise.all([
+5 -6
server/graphql/modules/textBank.ts
··· 1 - import { AuthenticationError } from 'apollo-server-express'; 2 - 3 1 import { isCoopErrorOfType } from '../../utils/errors.js'; 4 2 import { 5 3 type GQLMutationResolvers, 6 4 type GQLQueryResolvers, 7 5 } from '../generated.js'; 6 + import { unauthenticatedError } from '../utils/errors.js'; 8 7 9 8 const typeDefs = /* GraphQL */ ` 10 9 enum TextBankType { ··· 55 54 async textBank(_, { id }, context) { 56 55 const user = context.getUser(); 57 56 if (user == null) { 58 - throw new AuthenticationError('Authenticated user required'); 57 + throw unauthenticatedError('Authenticated user required'); 59 58 } 60 59 61 60 try { ··· 79 78 async createTextBank(_, params, context) { 80 79 const user = context.getUser(); 81 80 if (user == null) { 82 - throw new AuthenticationError('User required.'); 81 + throw unauthenticatedError('User required.'); 83 82 } 84 83 85 84 const { name, description, type, strings } = params.input; ··· 107 106 async updateTextBank(_, params, context) { 108 107 const user = context.getUser(); 109 108 if (user == null) { 110 - throw new AuthenticationError('User required.'); 109 + throw unauthenticatedError('User required.'); 111 110 } 112 111 113 112 const { id, name, description, type, strings } = params.input; ··· 135 134 async deleteTextBank(_, params, context) { 136 135 const user = context.getUser(); 137 136 if (user == null) { 138 - throw new AuthenticationError('Authenticated user required'); 137 + throw unauthenticatedError('Authenticated user required'); 139 138 } 140 139 141 140 try {
+14 -13
server/graphql/modules/user.ts
··· 1 - import { AuthenticationError, ForbiddenError } from 'apollo-server-express'; 2 1 import jwt from 'jsonwebtoken'; 3 2 4 3 import { ··· 9 8 type GQLUserResolvers, 10 9 } from '../generated.js'; 11 10 import { gqlSuccessResult } from '../utils/gqlResult.js'; 11 + 12 + import { forbiddenError, unauthenticatedError } from '../utils/errors.js'; 12 13 13 14 const typeDefs = /* GraphQL */ ` 14 15 enum UserRole { ··· 187 188 async user(_, { id }, context) { 188 189 const user = context.getUser(); 189 190 if (user == null) { 190 - throw new AuthenticationError('User required.'); 191 + throw unauthenticatedError('User required.'); 191 192 } 192 193 193 194 const { orgId } = user; ··· 199 200 async updateAccountInfo(_, params, context) { 200 201 const user = context.getUser(); 201 202 if (user == null) { 202 - throw new AuthenticationError('Authenticated user required'); 203 + throw unauthenticatedError('Authenticated user required'); 203 204 } 204 205 await context.dataSources.userAPI.updateAccountInfo(user, params); 205 206 return true; // TODO: return the updated user instead. ··· 207 208 async changePassword(_, params, context) { 208 209 const user = context.getUser(); 209 210 if (user == null) { 210 - throw new AuthenticationError('Authenticated user required'); 211 + throw unauthenticatedError('Authenticated user required'); 211 212 } 212 213 return context.dataSources.userAPI.changePassword(user, params.input); 213 214 }, 214 215 async deleteUser(_, params, context) { 215 216 const user = context.getUser(); 216 217 if (user == null) { 217 - throw new AuthenticationError('Authenticated user required'); 218 + throw unauthenticatedError('Authenticated user required'); 218 219 } 219 220 220 221 return context.dataSources.userAPI.deleteUser({ ··· 225 226 async addFavoriteRule(_, params, context) { 226 227 const user = context.getUser(); 227 228 if (user == null) { 228 - throw new AuthenticationError('User required.'); 229 + throw unauthenticatedError('User required.'); 229 230 } 230 231 await context.dataSources.userAPI.addFavoriteRule( 231 232 user.id, ··· 237 238 async removeFavoriteRule(_, params, context) { 238 239 const user = context.getUser(); 239 240 if (user == null) { 240 - throw new AuthenticationError('User required.'); 241 + throw unauthenticatedError('User required.'); 241 242 } 242 243 await context.dataSources.userAPI.removeFavoriteRule( 243 244 user.id, ··· 249 250 async addFavoriteMRTQueue(_, params, context) { 250 251 const user = context.getUser(); 251 252 if (user == null) { 252 - throw new AuthenticationError('User required.'); 253 + throw unauthenticatedError('User required.'); 253 254 } 254 255 await context.services.ManualReviewToolService.addFavoriteQueueForUser({ 255 256 userId: user.id, ··· 261 262 async removeFavoriteMRTQueue(_, params, context) { 262 263 const user = context.getUser(); 263 264 if (user == null) { 264 - throw new AuthenticationError('User required.'); 265 + throw unauthenticatedError('User required.'); 265 266 } 266 267 await context.services.ManualReviewToolService.removeFavoriteQueueForUser({ 267 268 userId: user.id, ··· 273 274 async setModeratorSafetySettings(_, params, context) { 274 275 const user = context.getUser(); 275 276 if (user == null) { 276 - throw new AuthenticationError('User required.'); 277 + throw unauthenticatedError('User required.'); 277 278 } 278 279 await context.services.UserManagementService.upsertUserInterfaceSettings({ 279 280 userId: user.id, ··· 287 288 async setMrtChartConfigurationSettings(_, params, context) { 288 289 const user = context.getUser(); 289 290 if (user == null) { 290 - throw new AuthenticationError('User required.'); 291 + throw unauthenticatedError('User required.'); 291 292 } 292 293 await context.services.UserManagementService.upsertUserInterfaceSettings({ 293 294 userId: user.id, ··· 342 343 try { 343 344 const authedUser = getUser(); 344 345 if (!authedUser || user.id !== authedUser.id) { 345 - throw new ForbiddenError('Must be signed in as this user to read JWT.'); 346 + throw forbiddenError('Must be signed in as this user to read JWT.'); 346 347 } 347 348 348 349 const { email, firstName, lastName, orgId } = user; ··· 393 394 async reviewableQueues(_, { queueIds }, context) { 394 395 const user = context.getUser(); 395 396 if (user == null) { 396 - throw new AuthenticationError('Authenticated user required'); 397 + throw unauthenticatedError('Authenticated user required'); 397 398 } 398 399 399 400 const queues =
+12 -22
server/graphql/resolvers.ts
··· 1 1 import { mergeResolvers } from '@graphql-tools/merge'; 2 - import { AuthenticationError } from 'apollo-server-core'; 3 2 import { type GraphQLFieldResolver } from 'graphql'; 4 3 import { type PassportContext } from 'graphql-passport'; 5 4 ··· 39 38 import { resolvers as textBankResolvers } from './modules/textBank.js'; 40 39 import { resolvers as userResolvers } from './modules/user.js'; 41 40 import { gqlErrorResult, gqlSuccessResult } from './utils/gqlResult.js'; 41 + import { forbiddenError, unauthenticatedError } from './utils/errors.js'; 42 42 43 43 // eslint-disable-next-line @typescript-eslint/no-restricted-types 44 44 export type Context = PassportContext<User, {}> & { ··· 166 166 async generatePasswordResetToken(_, { userId }, context) { 167 167 const user = context.getUser(); 168 168 if (user == null) { 169 - throw new AuthenticationError('Authenticated user required'); 169 + throw unauthenticatedError('Authenticated user required'); 170 170 } 171 171 172 172 if (!user.getPermissions().includes('MANAGE_ORG')) { 173 - throw new AuthenticationError( 174 - 'User does not have permission to generate password reset tokens', 175 - ); 173 + throw forbiddenError('User does not have permission to generate password reset tokens'); 176 174 } 177 175 178 176 const token = ··· 184 182 async updateRole(_, params, context) { 185 183 const user = context.getUser(); 186 184 if (user == null) { 187 - throw new AuthenticationError('Authenticated user required'); 185 + throw unauthenticatedError('Authenticated user required'); 188 186 } 189 187 190 188 await context.services.UserManagementService.updateUserRole({ ··· 203 201 async inviteUser(_, params, context) { 204 202 const user = context.getUser(); 205 203 if (user == null) { 206 - throw new AuthenticationError('Authenticated user required'); 204 + throw unauthenticatedError('Authenticated user required'); 207 205 } 208 206 209 207 if (!user.getPermissions().includes('MANAGE_ORG')) { 210 - throw new AuthenticationError( 211 - 'User does not have permission to invite users', 212 - ); 208 + throw forbiddenError('User does not have permission to invite users'); 213 209 } 214 210 215 211 const token = await context.dataSources.orgAPI.inviteUser( ··· 221 217 async deleteInvite(_, { id }, context) { 222 218 const user = context.getUser(); 223 219 if (user == null) { 224 - throw new AuthenticationError('Authenticated user required'); 220 + throw unauthenticatedError('Authenticated user required'); 225 221 } 226 222 227 223 if (!user.getPermissions().includes('MANAGE_ORG')) { 228 - throw new AuthenticationError( 229 - 'User does not have permission to delete invites', 230 - ); 224 + throw forbiddenError('User does not have permission to delete invites'); 231 225 } 232 226 233 227 return context.services.UserManagementService.deleteInvite(id, user.orgId); ··· 235 229 async approveUser(_, { id }, context) { 236 230 const user = context.getUser(); 237 231 if (user == null) { 238 - throw new AuthenticationError('Authenticated user required'); 232 + throw unauthenticatedError('Authenticated user required'); 239 233 } 240 234 241 235 if (!user.getPermissions().includes('MANAGE_ORG')) { 242 - throw new AuthenticationError( 243 - 'User does not have permission to approve users', 244 - ); 236 + throw forbiddenError('User does not have permission to approve users'); 245 237 } 246 238 247 239 return context.dataSources.userAPI.approveUser(id, user.orgId); ··· 249 241 async rejectUser(_, { id }, context) { 250 242 const user = context.getUser(); 251 243 if (user == null) { 252 - throw new AuthenticationError('Authenticated user required'); 244 + throw unauthenticatedError('Authenticated user required'); 253 245 } 254 246 255 247 if (!user.getPermissions().includes('MANAGE_ORG')) { 256 - throw new AuthenticationError( 257 - 'User does not have permission to reject users', 258 - ); 248 + throw forbiddenError('User does not have permission to reject users'); 259 249 } 260 250 261 251 return context.dataSources.userAPI.rejectUser(id, user.orgId);
+3 -2
server/graphql/utils/authorization.ts
··· 1 1 import { getDirective } from '@graphql-tools/utils'; 2 - import { AuthenticationError } from 'apollo-server-express'; 3 2 import { 4 3 defaultFieldResolver, 5 4 type GraphQLFieldConfig, ··· 8 7 } from 'graphql'; 9 8 10 9 import type { Context } from '../resolvers.js'; 10 + 11 + import { unauthenticatedError } from './errors.js'; 11 12 12 13 export function shouldSkipAuth( 13 14 schema: GraphQLSchema, ··· 34 35 info: GraphQLResolveInfo, 35 36 ) { 36 37 if (!context.getUser()) { 37 - throw new AuthenticationError('No user in context.'); 38 + throw unauthenticatedError('No user in context.'); 38 39 } 39 40 return originalResolver(source, args, context, info); 40 41 },
+10
server/graphql/utils/errors.ts
··· 1 + import { GraphQLError } from 'graphql'; 2 + 3 + export const unauthenticatedError = (message: string) => 4 + new GraphQLError(message, { extensions: { code: 'UNAUTHENTICATED' } }); 5 + 6 + export const forbiddenError = (message: string) => 7 + new GraphQLError(message, { extensions: { code: 'FORBIDDEN' } }); 8 + 9 + export const userInputError = (message: string) => 10 + new GraphQLError(message, { extensions: { code: 'BAD_USER_INPUT' } });
+6 -7
server/graphql/utils/paginationHandler.ts
··· 1 - import { UserInputError } from 'apollo-server-express'; 2 1 import { type GraphQLFieldResolver as Resolver } from 'graphql'; 3 2 4 3 import { type JSON } from '../../utils/json-schema-types.js'; 4 + 5 + import { userInputError } from './errors.js'; 5 6 6 7 /** 7 8 * A type describing the arguments a connection field receives in GraphQL. ··· 121 122 // https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#auto-id-error-cases 122 123 const { takeFrom, pageSize } = (() => { 123 124 if (first != null && last != null) { 124 - throw new UserInputError(`Cannot specify both first and last`); 125 + throw userInputError(`Cannot specify both first and last`); 125 126 } else if (first != null) { 126 127 return { takeFrom: 'start', pageSize: first } as const; 127 128 } else if (last != null) { ··· 132 133 })(); 133 134 134 135 if (pageSize <= 0) { 135 - throw new UserInputError('Page size must be a positive number.'); 136 + throw userInputError('Page size must be a positive number.'); 136 137 } 137 138 138 139 if (pageSize > maxPageSize) { 139 - throw new UserInputError( 140 - `Page size must be less than or equal to ${maxPageSize}.`, 141 - ); 140 + throw userInputError(`Page size must be less than or equal to ${maxPageSize}.`); 142 141 } 143 142 144 143 // Meanwhile, providing both a before and after cursor is also coherent ··· 147 146 // where the user explicitly provided only one cursor, but we synthesized 148 147 // the other. 149 148 if (before != null && after != null) { 150 - throw new UserInputError('Combining before and after is not supported.'); 149 + throw userInputError('Combining before and after is not supported.'); 151 150 } 152 151 153 152 // figure out the direction we're paginating in,
+1 -2
server/iocContainer/services/gqlDataSources.ts
··· 1 1 import type Bottle from '@ethanresnick/bottlejs'; 2 - import { DataSource } from 'apollo-datasource'; 3 2 4 3 import makeActionAPI, { 5 4 type ActionAPI, ··· 69 68 orgAPI: deps.OrgAPIDataSource, 70 69 ruleAPI: deps.RuleAPIDataSource, 71 70 userAPI: deps.UserAPIDataSource, 72 - notificationsAPI: new (class extends DataSource<unknown> { 71 + notificationsAPI: new (class { 73 72 private service = deps.NotificationsService; 74 73 public async getNotificationsForUser(id: string) { 75 74 return this.service.getNotificationsForUser(id);
+478 -383
server/package-lock.json
··· 9 9 "version": "1.0.0", 10 10 "license": "ISC", 11 11 "dependencies": { 12 + "@apollo/server": "^5.5.0", 13 + "@as-integrations/express4": "^1.1.2", 12 14 "@aws-sdk/client-s3": "^3.1017.0", 13 15 "@aws-sdk/client-secrets-manager": "^3.1017.0", 14 16 "@aws-sdk/client-ses": "^3.1017.0", ··· 43 45 "@types/yargs": "^17.0.32", 44 46 "ajv": "^8.18.0", 45 47 "ajv-draft-04": "^1.0.0", 46 - "apollo-datasource": "^3.3.0", 47 - "apollo-server-core": "^3.11.1", 48 - "apollo-server-express": "^3.10.3", 49 48 "bcryptjs": "^2.4.3", 50 49 "bullmq": "^5.0.0", 51 50 "cassandra-driver": "^4.8.0", ··· 63 62 "formdata-node": "^6.0.3", 64 63 "fuzzball": "^2.1.2", 65 64 "generic-pool": "^3.8.2", 66 - "graphql": "^16.0.1", 65 + "graphql": "^16.13.2", 67 66 "graphql-depth-limit": "^1.1.0", 68 67 "graphql-passport": "^0.6.4", 69 68 "graphql-scalars": "^1.19.0", ··· 105 104 "@eslint/js": "^9.39.4", 106 105 "@faker-js/faker": "^7.5.0", 107 106 "@types/cls-hooked": "^4.3.3", 107 + "@types/cors": "^2.8.19", 108 108 "@types/graphql-depth-limit": "^1.1.6", 109 109 "@types/jest": "^29.2.4", 110 110 "@types/js-yaml": "^4.0.5", ··· 143 143 "node": ">=0.10.0" 144 144 } 145 145 }, 146 + "node_modules/@apollo/cache-control-types": { 147 + "version": "1.0.3", 148 + "resolved": "https://registry.npmjs.org/@apollo/cache-control-types/-/cache-control-types-1.0.3.tgz", 149 + "integrity": "sha512-F17/vCp7QVwom9eG7ToauIKdAxpSoadsJnqIfyryLFSkLSOEqu+eC5Z3N8OXcUVStuOMcNHlyraRsA6rRICu4g==", 150 + "license": "MIT", 151 + "peerDependencies": { 152 + "graphql": "14.x || 15.x || 16.x" 153 + } 154 + }, 146 155 "node_modules/@apollo/protobufjs": { 147 156 "version": "1.2.7", 148 157 "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.7.tgz", ··· 167 176 "apollo-pbts": "bin/pbts" 168 177 } 169 178 }, 170 - "node_modules/@apollo/usage-reporting-protobuf": { 171 - "version": "4.1.1", 172 - "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", 173 - "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", 179 + "node_modules/@apollo/server": { 180 + "version": "5.5.0", 181 + "resolved": "https://registry.npmjs.org/@apollo/server/-/server-5.5.0.tgz", 182 + "integrity": "sha512-vWtodBOK/SZwBTJzItECOmLfL8E8pn/IdvP7pnxN5g2tny9iW4+9sxdajE798wV1H2+PYp/rRcl/soSHIBKMPw==", 183 + "license": "MIT", 174 184 "dependencies": { 175 - "@apollo/protobufjs": "1.2.7" 185 + "@apollo/cache-control-types": "^1.0.3", 186 + "@apollo/server-gateway-interface": "^2.0.0", 187 + "@apollo/usage-reporting-protobuf": "^4.1.1", 188 + "@apollo/utils.createhash": "^3.0.0", 189 + "@apollo/utils.fetcher": "^3.0.0", 190 + "@apollo/utils.isnodelike": "^3.0.0", 191 + "@apollo/utils.keyvaluecache": "^4.0.0", 192 + "@apollo/utils.logger": "^3.0.0", 193 + "@apollo/utils.usagereporting": "^2.1.0", 194 + "@apollo/utils.withrequired": "^3.0.0", 195 + "@graphql-tools/schema": "^10.0.0", 196 + "async-retry": "^1.2.1", 197 + "body-parser": "^2.2.2", 198 + "content-type": "^1.0.5", 199 + "cors": "^2.8.5", 200 + "finalhandler": "^2.1.0", 201 + "loglevel": "^1.6.8", 202 + "lru-cache": "^11.1.0", 203 + "negotiator": "^1.0.0", 204 + "uuid": "^11.1.0", 205 + "whatwg-mimetype": "^4.0.0" 206 + }, 207 + "engines": { 208 + "node": ">=20" 209 + }, 210 + "peerDependencies": { 211 + "graphql": "^16.11.0" 176 212 } 177 213 }, 178 - "node_modules/@apollo/utils.dropunuseddefinitions": { 179 - "version": "1.1.0", 180 - "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-1.1.0.tgz", 181 - "integrity": "sha512-jU1XjMr6ec9pPoL+BFWzEPW7VHHulVdGKMkPAMiCigpVIT11VmCbnij0bWob8uS3ODJ65tZLYKAh/55vLw2rbg==", 214 + "node_modules/@apollo/server-gateway-interface": { 215 + "version": "2.0.0", 216 + "resolved": "https://registry.npmjs.org/@apollo/server-gateway-interface/-/server-gateway-interface-2.0.0.tgz", 217 + "integrity": "sha512-3HEMD6fSantG2My3jWkb9dvfkF9vJ4BDLRjMgsnD790VINtuPaEp+h3Hg9HOHiWkML6QsOhnaRqZ+gvhp3y8Nw==", 218 + "license": "MIT", 219 + "dependencies": { 220 + "@apollo/usage-reporting-protobuf": "^4.1.1", 221 + "@apollo/utils.fetcher": "^3.0.0", 222 + "@apollo/utils.keyvaluecache": "^4.0.0", 223 + "@apollo/utils.logger": "^3.0.0" 224 + }, 225 + "peerDependencies": { 226 + "graphql": "14.x || 15.x || 16.x" 227 + } 228 + }, 229 + "node_modules/@apollo/server-gateway-interface/node_modules/@apollo/utils.keyvaluecache": { 230 + "version": "4.0.0", 231 + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz", 232 + "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==", 233 + "license": "MIT", 234 + "dependencies": { 235 + "@apollo/utils.logger": "^3.0.0", 236 + "lru-cache": "^11.0.0" 237 + }, 182 238 "engines": { 183 - "node": ">=12.13.0" 239 + "node": ">=20" 240 + } 241 + }, 242 + "node_modules/@apollo/server-gateway-interface/node_modules/@apollo/utils.logger": { 243 + "version": "3.0.0", 244 + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz", 245 + "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==", 246 + "license": "MIT", 247 + "engines": { 248 + "node": ">=16" 249 + } 250 + }, 251 + "node_modules/@apollo/server-gateway-interface/node_modules/lru-cache": { 252 + "version": "11.3.5", 253 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", 254 + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", 255 + "license": "BlueOak-1.0.0", 256 + "engines": { 257 + "node": "20 || >=22" 258 + } 259 + }, 260 + "node_modules/@apollo/server/node_modules/@apollo/utils.dropunuseddefinitions": { 261 + "version": "2.0.1", 262 + "resolved": "https://registry.npmjs.org/@apollo/utils.dropunuseddefinitions/-/utils.dropunuseddefinitions-2.0.1.tgz", 263 + "integrity": "sha512-EsPIBqsSt2BwDsv8Wu76LK5R1KtsVkNoO4b0M5aK0hx+dGg9xJXuqlr7Fo34Dl+y83jmzn+UvEW+t1/GP2melA==", 264 + "license": "MIT", 265 + "engines": { 266 + "node": ">=14" 184 267 }, 185 268 "peerDependencies": { 186 269 "graphql": "14.x || 15.x || 16.x" 187 270 } 188 271 }, 189 - "node_modules/@apollo/utils.keyvaluecache": { 190 - "version": "1.0.2", 191 - "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-1.0.2.tgz", 192 - "integrity": "sha512-p7PVdLPMnPzmXSQVEsy27cYEjVON+SH/Wb7COyW3rQN8+wJgT1nv9jZouYtztWW8ZgTkii5T6tC9qfoDREd4mg==", 272 + "node_modules/@apollo/server/node_modules/@apollo/utils.keyvaluecache": { 273 + "version": "4.0.0", 274 + "resolved": "https://registry.npmjs.org/@apollo/utils.keyvaluecache/-/utils.keyvaluecache-4.0.0.tgz", 275 + "integrity": "sha512-mKw1myRUkQsGPNB+9bglAuhviodJ2L2MRYLTafCMw5BIo7nbvCPNCkLnIHjZ1NOzH7SnMAr5c9LmXiqsgYqLZw==", 276 + "license": "MIT", 193 277 "dependencies": { 194 - "@apollo/utils.logger": "^1.0.0", 195 - "lru-cache": "7.10.1 - 7.13.1" 278 + "@apollo/utils.logger": "^3.0.0", 279 + "lru-cache": "^11.0.0" 280 + }, 281 + "engines": { 282 + "node": ">=20" 196 283 } 197 284 }, 198 - "node_modules/@apollo/utils.logger": { 199 - "version": "1.0.1", 200 - "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-1.0.1.tgz", 201 - "integrity": "sha512-XdlzoY7fYNK4OIcvMD2G94RoFZbzTQaNP0jozmqqMudmaGo2I/2Jx71xlDJ801mWA/mbYRihyaw6KJii7k5RVA==" 285 + "node_modules/@apollo/server/node_modules/@apollo/utils.logger": { 286 + "version": "3.0.0", 287 + "resolved": "https://registry.npmjs.org/@apollo/utils.logger/-/utils.logger-3.0.0.tgz", 288 + "integrity": "sha512-M8V8JOTH0F2qEi+ktPfw4RL7MvUycDfKp7aEap2eWXfL5SqWHN6jTLbj5f5fj1cceHpyaUSOZlvlaaryaxZAmg==", 289 + "license": "MIT", 290 + "engines": { 291 + "node": ">=16" 292 + } 202 293 }, 203 - "node_modules/@apollo/utils.printwithreducedwhitespace": { 204 - "version": "1.1.0", 205 - "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-1.1.0.tgz", 206 - "integrity": "sha512-GfFSkAv3n1toDZ4V6u2d7L4xMwLA+lv+6hqXicMN9KELSJ9yy9RzuEXaX73c/Ry+GzRsBy/fdSUGayGqdHfT2Q==", 294 + "node_modules/@apollo/server/node_modules/@apollo/utils.printwithreducedwhitespace": { 295 + "version": "2.0.1", 296 + "resolved": "https://registry.npmjs.org/@apollo/utils.printwithreducedwhitespace/-/utils.printwithreducedwhitespace-2.0.1.tgz", 297 + "integrity": "sha512-9M4LUXV/fQBh8vZWlLvb/HyyhjJ77/I5ZKu+NBWV/BmYGyRmoEP9EVAy7LCVoY3t8BDcyCAGfxJaLFCSuQkPUg==", 298 + "license": "MIT", 207 299 "engines": { 208 - "node": ">=12.13.0" 300 + "node": ">=14" 209 301 }, 210 302 "peerDependencies": { 211 303 "graphql": "14.x || 15.x || 16.x" 212 304 } 213 305 }, 214 - "node_modules/@apollo/utils.removealiases": { 215 - "version": "1.0.0", 216 - "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-1.0.0.tgz", 217 - "integrity": "sha512-6cM8sEOJW2LaGjL/0vHV0GtRaSekrPQR4DiywaApQlL9EdROASZU5PsQibe2MWeZCOhNrPRuHh4wDMwPsWTn8A==", 306 + "node_modules/@apollo/server/node_modules/@apollo/utils.removealiases": { 307 + "version": "2.0.1", 308 + "resolved": "https://registry.npmjs.org/@apollo/utils.removealiases/-/utils.removealiases-2.0.1.tgz", 309 + "integrity": "sha512-0joRc2HBO4u594Op1nev+mUF6yRnxoUH64xw8x3bX7n8QBDYdeYgY4tF0vJReTy+zdn2xv6fMsquATSgC722FA==", 310 + "license": "MIT", 218 311 "engines": { 219 - "node": ">=12.13.0" 312 + "node": ">=14" 220 313 }, 221 314 "peerDependencies": { 222 315 "graphql": "14.x || 15.x || 16.x" 223 316 } 224 317 }, 225 - "node_modules/@apollo/utils.sortast": { 226 - "version": "1.1.0", 227 - "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-1.1.0.tgz", 228 - "integrity": "sha512-VPlTsmUnOwzPK5yGZENN069y6uUHgeiSlpEhRnLFYwYNoJHsuJq2vXVwIaSmts015WTPa2fpz1inkLYByeuRQA==", 318 + "node_modules/@apollo/server/node_modules/@apollo/utils.sortast": { 319 + "version": "2.0.1", 320 + "resolved": "https://registry.npmjs.org/@apollo/utils.sortast/-/utils.sortast-2.0.1.tgz", 321 + "integrity": "sha512-eciIavsWpJ09za1pn37wpsCGrQNXUhM0TktnZmHwO+Zy9O4fu/WdB4+5BvVhFiZYOXvfjzJUcc+hsIV8RUOtMw==", 322 + "license": "MIT", 229 323 "dependencies": { 230 324 "lodash.sortby": "^4.7.0" 231 325 }, 232 326 "engines": { 233 - "node": ">=12.13.0" 327 + "node": ">=14" 234 328 }, 235 329 "peerDependencies": { 236 330 "graphql": "14.x || 15.x || 16.x" 237 331 } 238 332 }, 239 - "node_modules/@apollo/utils.stripsensitiveliterals": { 240 - "version": "1.2.0", 241 - "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-1.2.0.tgz", 242 - "integrity": "sha512-E41rDUzkz/cdikM5147d8nfCFVKovXxKBcjvLEQ7bjZm/cg9zEcXvS6vFY8ugTubI3fn6zoqo0CyU8zT+BGP9w==", 333 + "node_modules/@apollo/server/node_modules/@apollo/utils.stripsensitiveliterals": { 334 + "version": "2.0.1", 335 + "resolved": "https://registry.npmjs.org/@apollo/utils.stripsensitiveliterals/-/utils.stripsensitiveliterals-2.0.1.tgz", 336 + "integrity": "sha512-QJs7HtzXS/JIPMKWimFnUMK7VjkGQTzqD9bKD1h3iuPAqLsxd0mUNVbkYOPTsDhUKgcvUOfOqOJWYohAKMvcSA==", 337 + "license": "MIT", 243 338 "engines": { 244 - "node": ">=12.13.0" 339 + "node": ">=14" 245 340 }, 246 341 "peerDependencies": { 247 342 "graphql": "14.x || 15.x || 16.x" 248 343 } 249 344 }, 250 - "node_modules/@apollo/utils.usagereporting": { 251 - "version": "1.0.1", 252 - "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-1.0.1.tgz", 253 - "integrity": "sha512-6dk+0hZlnDbahDBB2mP/PZ5ybrtCJdLMbeNJD+TJpKyZmSY6bA3SjI8Cr2EM9QA+AdziywuWg+SgbWUF3/zQqQ==", 345 + "node_modules/@apollo/server/node_modules/@apollo/utils.usagereporting": { 346 + "version": "2.1.0", 347 + "resolved": "https://registry.npmjs.org/@apollo/utils.usagereporting/-/utils.usagereporting-2.1.0.tgz", 348 + "integrity": "sha512-LPSlBrn+S17oBy5eWkrRSGb98sWmnEzo3DPTZgp8IQc8sJe0prDgDuppGq4NeQlpoqEHz0hQeYHAOA0Z3aQsxQ==", 349 + "license": "MIT", 254 350 "dependencies": { 255 - "@apollo/usage-reporting-protobuf": "^4.0.0", 256 - "@apollo/utils.dropunuseddefinitions": "^1.1.0", 257 - "@apollo/utils.printwithreducedwhitespace": "^1.1.0", 258 - "@apollo/utils.removealiases": "1.0.0", 259 - "@apollo/utils.sortast": "^1.1.0", 260 - "@apollo/utils.stripsensitiveliterals": "^1.2.0" 351 + "@apollo/usage-reporting-protobuf": "^4.1.0", 352 + "@apollo/utils.dropunuseddefinitions": "^2.0.1", 353 + "@apollo/utils.printwithreducedwhitespace": "^2.0.1", 354 + "@apollo/utils.removealiases": "2.0.1", 355 + "@apollo/utils.sortast": "^2.0.1", 356 + "@apollo/utils.stripsensitiveliterals": "^2.0.1" 261 357 }, 262 358 "engines": { 263 - "node": ">=12.13.0" 359 + "node": ">=14" 264 360 }, 265 361 "peerDependencies": { 266 362 "graphql": "14.x || 15.x || 16.x" 267 363 } 268 364 }, 269 - "node_modules/@apollographql/apollo-tools": { 270 - "version": "0.5.4", 271 - "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.5.4.tgz", 272 - "integrity": "sha512-shM3q7rUbNyXVVRkQJQseXv6bnYM3BUma/eZhwXR4xsuM+bqWnJKvW7SAfRjP7LuSCocrexa5AXhjjawNHrIlw==", 365 + "node_modules/@apollo/server/node_modules/@graphql-tools/merge": { 366 + "version": "9.1.8", 367 + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.8.tgz", 368 + "integrity": "sha512-25V7WDrODo1cPrmuUCrqf5qlMA4a/Ow4aHaqJ1MnTUaluwsV3UiqzCHWux3HSLb0H63mkoZiuOrU5xJhxRcoCg==", 369 + "license": "MIT", 370 + "dependencies": { 371 + "@graphql-tools/utils": "^11.0.1", 372 + "tslib": "^2.4.0" 373 + }, 374 + "engines": { 375 + "node": ">=16.0.0" 376 + }, 377 + "peerDependencies": { 378 + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 379 + } 380 + }, 381 + "node_modules/@apollo/server/node_modules/@graphql-tools/schema": { 382 + "version": "10.0.32", 383 + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.32.tgz", 384 + "integrity": "sha512-kJ1Qn20MPnlaEVH37639E6rzQ1tEtr6XTUhNdR4EKydl+FijtLhWX2WLZbGnvrYuG8XUcMxsZU9mRRYYNvK02w==", 385 + "license": "MIT", 386 + "dependencies": { 387 + "@graphql-tools/merge": "^9.1.8", 388 + "@graphql-tools/utils": "^11.0.1", 389 + "tslib": "^2.4.0" 390 + }, 273 391 "engines": { 274 - "node": ">=8", 275 - "npm": ">=6" 392 + "node": ">=16.0.0" 276 393 }, 277 394 "peerDependencies": { 278 - "graphql": "^14.2.1 || ^15.0.0 || ^16.0.0" 395 + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 396 + } 397 + }, 398 + "node_modules/@apollo/server/node_modules/@graphql-tools/utils": { 399 + "version": "11.0.1", 400 + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-11.0.1.tgz", 401 + "integrity": "sha512-pNyCOb95ab/z3zkkiPwIPYxigX7IcpyFVcgD1XACDEvg/7yGnKCESx3k/XHEeneKYx/aWKGzEh/uuf6M6Q8HOw==", 402 + "license": "MIT", 403 + "dependencies": { 404 + "@graphql-typed-document-node/core": "^3.1.1", 405 + "@whatwg-node/promise-helpers": "^1.0.0", 406 + "cross-inspect": "1.0.1", 407 + "tslib": "^2.4.0" 408 + }, 409 + "engines": { 410 + "node": ">=16.0.0" 411 + }, 412 + "peerDependencies": { 413 + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 414 + } 415 + }, 416 + "node_modules/@apollo/server/node_modules/body-parser": { 417 + "version": "2.2.2", 418 + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", 419 + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", 420 + "license": "MIT", 421 + "dependencies": { 422 + "bytes": "^3.1.2", 423 + "content-type": "^1.0.5", 424 + "debug": "^4.4.3", 425 + "http-errors": "^2.0.0", 426 + "iconv-lite": "^0.7.0", 427 + "on-finished": "^2.4.1", 428 + "qs": "^6.14.1", 429 + "raw-body": "^3.0.1", 430 + "type-is": "^2.0.1" 431 + }, 432 + "engines": { 433 + "node": ">=18" 434 + }, 435 + "funding": { 436 + "type": "opencollective", 437 + "url": "https://opencollective.com/express" 438 + } 439 + }, 440 + "node_modules/@apollo/server/node_modules/debug": { 441 + "version": "4.4.3", 442 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 443 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 444 + "license": "MIT", 445 + "dependencies": { 446 + "ms": "^2.1.3" 447 + }, 448 + "engines": { 449 + "node": ">=6.0" 450 + }, 451 + "peerDependenciesMeta": { 452 + "supports-color": { 453 + "optional": true 454 + } 455 + } 456 + }, 457 + "node_modules/@apollo/server/node_modules/finalhandler": { 458 + "version": "2.1.1", 459 + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", 460 + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", 461 + "license": "MIT", 462 + "dependencies": { 463 + "debug": "^4.4.0", 464 + "encodeurl": "^2.0.0", 465 + "escape-html": "^1.0.3", 466 + "on-finished": "^2.4.1", 467 + "parseurl": "^1.3.3", 468 + "statuses": "^2.0.1" 469 + }, 470 + "engines": { 471 + "node": ">= 18.0.0" 472 + }, 473 + "funding": { 474 + "type": "opencollective", 475 + "url": "https://opencollective.com/express" 476 + } 477 + }, 478 + "node_modules/@apollo/server/node_modules/iconv-lite": { 479 + "version": "0.7.2", 480 + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", 481 + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", 482 + "license": "MIT", 483 + "dependencies": { 484 + "safer-buffer": ">= 2.1.2 < 3.0.0" 485 + }, 486 + "engines": { 487 + "node": ">=0.10.0" 488 + }, 489 + "funding": { 490 + "type": "opencollective", 491 + "url": "https://opencollective.com/express" 492 + } 493 + }, 494 + "node_modules/@apollo/server/node_modules/lru-cache": { 495 + "version": "11.3.5", 496 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", 497 + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", 498 + "license": "BlueOak-1.0.0", 499 + "engines": { 500 + "node": "20 || >=22" 501 + } 502 + }, 503 + "node_modules/@apollo/server/node_modules/media-typer": { 504 + "version": "1.1.0", 505 + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", 506 + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", 507 + "license": "MIT", 508 + "engines": { 509 + "node": ">= 0.8" 510 + } 511 + }, 512 + "node_modules/@apollo/server/node_modules/mime-db": { 513 + "version": "1.54.0", 514 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 515 + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 516 + "license": "MIT", 517 + "engines": { 518 + "node": ">= 0.6" 519 + } 520 + }, 521 + "node_modules/@apollo/server/node_modules/mime-types": { 522 + "version": "3.0.2", 523 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", 524 + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", 525 + "license": "MIT", 526 + "dependencies": { 527 + "mime-db": "^1.54.0" 528 + }, 529 + "engines": { 530 + "node": ">=18" 531 + }, 532 + "funding": { 533 + "type": "opencollective", 534 + "url": "https://opencollective.com/express" 535 + } 536 + }, 537 + "node_modules/@apollo/server/node_modules/ms": { 538 + "version": "2.1.3", 539 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 540 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 541 + "license": "MIT" 542 + }, 543 + "node_modules/@apollo/server/node_modules/negotiator": { 544 + "version": "1.0.0", 545 + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", 546 + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", 547 + "license": "MIT", 548 + "engines": { 549 + "node": ">= 0.6" 550 + } 551 + }, 552 + "node_modules/@apollo/server/node_modules/raw-body": { 553 + "version": "3.0.2", 554 + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", 555 + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", 556 + "license": "MIT", 557 + "dependencies": { 558 + "bytes": "~3.1.2", 559 + "http-errors": "~2.0.1", 560 + "iconv-lite": "~0.7.0", 561 + "unpipe": "~1.0.0" 562 + }, 563 + "engines": { 564 + "node": ">= 0.10" 565 + } 566 + }, 567 + "node_modules/@apollo/server/node_modules/type-is": { 568 + "version": "2.0.1", 569 + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", 570 + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", 571 + "license": "MIT", 572 + "dependencies": { 573 + "content-type": "^1.0.5", 574 + "media-typer": "^1.1.0", 575 + "mime-types": "^3.0.0" 576 + }, 577 + "engines": { 578 + "node": ">= 0.6" 579 + } 580 + }, 581 + "node_modules/@apollo/server/node_modules/uuid": { 582 + "version": "11.1.0", 583 + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", 584 + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", 585 + "funding": [ 586 + "https://github.com/sponsors/broofa", 587 + "https://github.com/sponsors/ctavan" 588 + ], 589 + "license": "MIT", 590 + "bin": { 591 + "uuid": "dist/esm/bin/uuid" 592 + } 593 + }, 594 + "node_modules/@apollo/server/node_modules/whatwg-mimetype": { 595 + "version": "4.0.0", 596 + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", 597 + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", 598 + "license": "MIT", 599 + "engines": { 600 + "node": ">=18" 601 + } 602 + }, 603 + "node_modules/@apollo/usage-reporting-protobuf": { 604 + "version": "4.1.1", 605 + "resolved": "https://registry.npmjs.org/@apollo/usage-reporting-protobuf/-/usage-reporting-protobuf-4.1.1.tgz", 606 + "integrity": "sha512-u40dIUePHaSKVshcedO7Wp+mPiZsaU6xjv9J+VyxpoU/zL6Jle+9zWeG98tr/+SZ0nZ4OXhrbb8SNr0rAPpIDA==", 607 + "dependencies": { 608 + "@apollo/protobufjs": "1.2.7" 279 609 } 280 610 }, 281 - "node_modules/@apollographql/graphql-playground-html": { 282 - "version": "1.6.29", 283 - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.29.tgz", 284 - "integrity": "sha512-xCcXpoz52rI4ksJSdOCxeOCn2DLocxwHf9dVT/Q90Pte1LX+LY+91SFtJF3KXVHH8kEin+g1KKCQPKBjZJfWNA==", 611 + "node_modules/@apollo/utils.createhash": { 612 + "version": "3.0.1", 613 + "resolved": "https://registry.npmjs.org/@apollo/utils.createhash/-/utils.createhash-3.0.1.tgz", 614 + "integrity": "sha512-CKrlySj4eQYftBE5MJ8IzKwIibQnftDT7yGfsJy5KSEEnLlPASX0UTpbKqkjlVEwPPd4mEwI7WOM7XNxEuO05A==", 615 + "license": "MIT", 285 616 "dependencies": { 286 - "xss": "^1.0.8" 617 + "@apollo/utils.isnodelike": "^3.0.0", 618 + "sha.js": "^2.4.11" 619 + }, 620 + "engines": { 621 + "node": ">=16" 622 + } 623 + }, 624 + "node_modules/@apollo/utils.fetcher": { 625 + "version": "3.1.0", 626 + "resolved": "https://registry.npmjs.org/@apollo/utils.fetcher/-/utils.fetcher-3.1.0.tgz", 627 + "integrity": "sha512-Z3QAyrsQkvrdTuHAFwWDNd+0l50guwoQUoaDQssLOjkmnmVuvXlJykqlEJolio+4rFwBnWdoY1ByFdKaQEcm7A==", 628 + "license": "MIT", 629 + "engines": { 630 + "node": ">=16" 631 + } 632 + }, 633 + "node_modules/@apollo/utils.isnodelike": { 634 + "version": "3.0.0", 635 + "resolved": "https://registry.npmjs.org/@apollo/utils.isnodelike/-/utils.isnodelike-3.0.0.tgz", 636 + "integrity": "sha512-xrjyjfkzunZ0DeF6xkHaK5IKR8F1FBq6qV+uZ+h9worIF/2YSzA0uoBxGv6tbTeo9QoIQnRW4PVFzGix5E7n/g==", 637 + "license": "MIT", 638 + "engines": { 639 + "node": ">=16" 640 + } 641 + }, 642 + "node_modules/@apollo/utils.withrequired": { 643 + "version": "3.0.0", 644 + "resolved": "https://registry.npmjs.org/@apollo/utils.withrequired/-/utils.withrequired-3.0.0.tgz", 645 + "integrity": "sha512-aaxeavfJ+RHboh7c2ofO5HHtQobGX4AgUujXP4CXpREHp9fQ9jPi6K9T1jrAKe7HIipoP0OJ1gd6JamSkFIpvA==", 646 + "license": "MIT", 647 + "engines": { 648 + "node": ">=16" 649 + } 650 + }, 651 + "node_modules/@as-integrations/express4": { 652 + "version": "1.1.2", 653 + "resolved": "https://registry.npmjs.org/@as-integrations/express4/-/express4-1.1.2.tgz", 654 + "integrity": "sha512-PGeMcwoOKdYnZ4LtsmM7aLNoel3tbK8wKnfyahdRau1qb7wLbuaXB35zg3w34Ov4bm3WJtO3yzd8Bw5jVE+aIQ==", 655 + "license": "MIT", 656 + "engines": { 657 + "node": ">=20" 658 + }, 659 + "peerDependencies": { 660 + "@apollo/server": "^4.0.0 || ^5.0.0", 661 + "express": "^4.0.0" 287 662 } 288 663 }, 289 664 "node_modules/@assemblyscript/loader": { ··· 2240 2615 "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 2241 2616 } 2242 2617 }, 2243 - "node_modules/@graphql-tools/mock": { 2244 - "version": "8.7.20", 2245 - "resolved": "https://registry.npmjs.org/@graphql-tools/mock/-/mock-8.7.20.tgz", 2246 - "integrity": "sha512-ljcHSJWjC/ZyzpXd5cfNhPI7YljRVvabKHPzKjEs5ElxWu2cdlLGvyNYepApXDsM/OJG/2xuhGM+9GWu5gEAPQ==", 2247 - "dependencies": { 2248 - "@graphql-tools/schema": "^9.0.18", 2249 - "@graphql-tools/utils": "^9.2.1", 2250 - "fast-json-stable-stringify": "^2.1.0", 2251 - "tslib": "^2.4.0" 2252 - }, 2253 - "peerDependencies": { 2254 - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 2255 - } 2256 - }, 2257 - "node_modules/@graphql-tools/mock/node_modules/@graphql-tools/schema": { 2258 - "version": "9.0.19", 2259 - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-9.0.19.tgz", 2260 - "integrity": "sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w==", 2261 - "dependencies": { 2262 - "@graphql-tools/merge": "^8.4.1", 2263 - "@graphql-tools/utils": "^9.2.1", 2264 - "tslib": "^2.4.0", 2265 - "value-or-promise": "^1.0.12" 2266 - }, 2267 - "peerDependencies": { 2268 - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" 2269 - } 2270 - }, 2271 - "node_modules/@graphql-tools/mock/node_modules/value-or-promise": { 2272 - "version": "1.0.12", 2273 - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", 2274 - "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", 2275 - "engines": { 2276 - "node": ">=12" 2277 - } 2278 - }, 2279 2618 "node_modules/@graphql-tools/schema": { 2280 2619 "version": "8.5.1", 2281 2620 "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.5.1.tgz", ··· 2973 3312 "engines": { 2974 3313 "node": "^14.15.0 || ^16.10.0 || >=18.0.0" 2975 3314 } 2976 - }, 2977 - "node_modules/@josephg/resolvable": { 2978 - "version": "1.0.1", 2979 - "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", 2980 - "integrity": "sha512-CtzORUwWTTOTqfVtHaKRJ0I1kNQd1bpn3sUh8I3nJDVY+5/M/Oe1DnEWzPQvqq/xPIIkzzzIP7mfCoAjFRvDhg==" 2981 3315 }, 2982 3316 "node_modules/@jridgewell/gen-mapping": { 2983 3317 "version": "0.3.13", ··· 10480 10814 "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 10481 10815 "dev": true 10482 10816 }, 10483 - "node_modules/@types/accepts": { 10484 - "version": "1.3.5", 10485 - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", 10486 - "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", 10487 - "dependencies": { 10488 - "@types/node": "*" 10489 - } 10490 - }, 10491 10817 "node_modules/@types/babel__core": { 10492 10818 "version": "7.20.5", 10493 10819 "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", ··· 10589 10915 "dev": true 10590 10916 }, 10591 10917 "node_modules/@types/cors": { 10592 - "version": "2.8.12", 10593 - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", 10594 - "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==" 10918 + "version": "2.8.19", 10919 + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", 10920 + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", 10921 + "dev": true, 10922 + "license": "MIT", 10923 + "dependencies": { 10924 + "@types/node": "*" 10925 + } 10595 10926 }, 10596 10927 "node_modules/@types/debug": { 10597 10928 "version": "4.1.12", ··· 11811 12142 "url": "https://opencollective.com/typescript-eslint" 11812 12143 } 11813 12144 }, 12145 + "node_modules/@whatwg-node/promise-helpers": { 12146 + "version": "1.3.2", 12147 + "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", 12148 + "integrity": "sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==", 12149 + "license": "MIT", 12150 + "dependencies": { 12151 + "tslib": "^2.6.3" 12152 + }, 12153 + "engines": { 12154 + "node": ">=16.0.0" 12155 + } 12156 + }, 11814 12157 "node_modules/@xmldom/is-dom-node": { 11815 12158 "version": "1.0.1", 11816 12159 "resolved": "https://registry.npmjs.org/@xmldom/is-dom-node/-/is-dom-node-1.0.1.tgz", ··· 12015 12358 "url": "https://github.com/sponsors/jonschlinkert" 12016 12359 } 12017 12360 }, 12018 - "node_modules/apollo-datasource": { 12019 - "version": "3.3.2", 12020 - "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-3.3.2.tgz", 12021 - "integrity": "sha512-L5TiS8E2Hn/Yz7SSnWIVbZw0ZfEIXZCa5VUiVxD9P53JvSrf4aStvsFDlGWPvpIdCR+aly2CfoB79B9/JjKFqg==", 12022 - "deprecated": "The `apollo-datasource` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12023 - "dependencies": { 12024 - "@apollo/utils.keyvaluecache": "^1.0.1", 12025 - "apollo-server-env": "^4.2.1" 12026 - }, 12027 - "engines": { 12028 - "node": ">=12.0" 12029 - } 12030 - }, 12031 - "node_modules/apollo-reporting-protobuf": { 12032 - "version": "3.4.0", 12033 - "resolved": "https://registry.npmjs.org/apollo-reporting-protobuf/-/apollo-reporting-protobuf-3.4.0.tgz", 12034 - "integrity": "sha512-h0u3EbC/9RpihWOmcSsvTW2O6RXVaD/mPEjfrPkxRPTEPWqncsgOoRJw+wih4OqfH3PvTJvoEIf4LwKrUaqWog==", 12035 - "deprecated": "The `apollo-reporting-protobuf` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/usage-reporting-protobuf` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12036 - "dependencies": { 12037 - "@apollo/protobufjs": "1.2.6" 12038 - } 12039 - }, 12040 - "node_modules/apollo-reporting-protobuf/node_modules/@apollo/protobufjs": { 12041 - "version": "1.2.6", 12042 - "resolved": "https://registry.npmjs.org/@apollo/protobufjs/-/protobufjs-1.2.6.tgz", 12043 - "integrity": "sha512-Wqo1oSHNUj/jxmsVp4iR3I480p6qdqHikn38lKrFhfzcDJ7lwd7Ck7cHRl4JE81tWNArl77xhnG/OkZhxKBYOw==", 12044 - "hasInstallScript": true, 12045 - "dependencies": { 12046 - "@protobufjs/aspromise": "^1.1.2", 12047 - "@protobufjs/base64": "^1.1.2", 12048 - "@protobufjs/codegen": "^2.0.4", 12049 - "@protobufjs/eventemitter": "^1.1.0", 12050 - "@protobufjs/fetch": "^1.1.0", 12051 - "@protobufjs/float": "^1.0.2", 12052 - "@protobufjs/inquire": "^1.1.0", 12053 - "@protobufjs/path": "^1.1.2", 12054 - "@protobufjs/pool": "^1.1.0", 12055 - "@protobufjs/utf8": "^1.1.0", 12056 - "@types/long": "^4.0.0", 12057 - "@types/node": "^10.1.0", 12058 - "long": "^4.0.0" 12059 - }, 12060 - "bin": { 12061 - "apollo-pbjs": "bin/pbjs", 12062 - "apollo-pbts": "bin/pbts" 12063 - } 12064 - }, 12065 - "node_modules/apollo-reporting-protobuf/node_modules/@types/node": { 12066 - "version": "10.17.60", 12067 - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", 12068 - "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" 12069 - }, 12070 - "node_modules/apollo-server-core": { 12071 - "version": "3.13.0", 12072 - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-3.13.0.tgz", 12073 - "integrity": "sha512-v/g6DR6KuHn9DYSdtQijz8dLOkP78I5JSVJzPkARhDbhpH74QNwrQ2PP2URAPPEDJ2EeZNQDX8PvbYkAKqg+kg==", 12074 - "dependencies": { 12075 - "@apollo/utils.keyvaluecache": "^1.0.1", 12076 - "@apollo/utils.logger": "^1.0.0", 12077 - "@apollo/utils.usagereporting": "^1.0.0", 12078 - "@apollographql/apollo-tools": "^0.5.3", 12079 - "@apollographql/graphql-playground-html": "1.6.29", 12080 - "@graphql-tools/mock": "^8.1.2", 12081 - "@graphql-tools/schema": "^8.0.0", 12082 - "@josephg/resolvable": "^1.0.0", 12083 - "apollo-datasource": "^3.3.2", 12084 - "apollo-reporting-protobuf": "^3.4.0", 12085 - "apollo-server-env": "^4.2.1", 12086 - "apollo-server-errors": "^3.3.1", 12087 - "apollo-server-plugin-base": "^3.7.2", 12088 - "apollo-server-types": "^3.8.0", 12089 - "async-retry": "^1.2.1", 12090 - "fast-json-stable-stringify": "^2.1.0", 12091 - "graphql-tag": "^2.11.0", 12092 - "loglevel": "^1.6.8", 12093 - "lru-cache": "^6.0.0", 12094 - "node-abort-controller": "^3.0.1", 12095 - "sha.js": "^2.4.11", 12096 - "uuid": "^9.0.0", 12097 - "whatwg-mimetype": "^3.0.0" 12098 - }, 12099 - "engines": { 12100 - "node": ">=12.0" 12101 - }, 12102 - "peerDependencies": { 12103 - "graphql": "^15.3.0 || ^16.0.0" 12104 - } 12105 - }, 12106 - "node_modules/apollo-server-core/node_modules/lru-cache": { 12107 - "version": "6.0.0", 12108 - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 12109 - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 12110 - "dependencies": { 12111 - "yallist": "^4.0.0" 12112 - }, 12113 - "engines": { 12114 - "node": ">=10" 12115 - } 12116 - }, 12117 - "node_modules/apollo-server-core/node_modules/uuid": { 12118 - "version": "9.0.0", 12119 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", 12120 - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", 12121 - "bin": { 12122 - "uuid": "dist/bin/uuid" 12123 - } 12124 - }, 12125 - "node_modules/apollo-server-core/node_modules/yallist": { 12126 - "version": "4.0.0", 12127 - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 12128 - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" 12129 - }, 12130 - "node_modules/apollo-server-env": { 12131 - "version": "4.2.1", 12132 - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-4.2.1.tgz", 12133 - "integrity": "sha512-vm/7c7ld+zFMxibzqZ7SSa5tBENc4B0uye9LTfjJwGoQFY5xsUPH5FpO5j0bMUDZ8YYNbrF9SNtzc5Cngcr90g==", 12134 - "deprecated": "The `apollo-server-env` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/utils.fetcher` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12135 - "dependencies": { 12136 - "node-fetch": "^2.6.7" 12137 - }, 12138 - "engines": { 12139 - "node": ">=12.0" 12140 - } 12141 - }, 12142 - "node_modules/apollo-server-errors": { 12143 - "version": "3.3.1", 12144 - "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-3.3.1.tgz", 12145 - "integrity": "sha512-xnZJ5QWs6FixHICXHxUfm+ZWqqxrNuPlQ+kj5m6RtEgIpekOPssH/SD9gf2B4HuWV0QozorrygwZnux8POvyPA==", 12146 - "deprecated": "The `apollo-server-errors` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12147 - "engines": { 12148 - "node": ">=12.0" 12149 - }, 12150 - "peerDependencies": { 12151 - "graphql": "^15.3.0 || ^16.0.0" 12152 - } 12153 - }, 12154 - "node_modules/apollo-server-express": { 12155 - "version": "3.13.0", 12156 - "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-3.13.0.tgz", 12157 - "integrity": "sha512-iSxICNbDUyebOuM8EKb3xOrpIwOQgKxGbR2diSr4HP3IW8T3njKFOoMce50vr+moOCe1ev8BnLcw9SNbuUtf7g==", 12158 - "deprecated": "The `apollo-server-express` package is part of Apollo Server v2 and v3, which are now end-of-life (as of October 22nd 2023 and October 22nd 2024, respectively). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12159 - "license": "MIT", 12160 - "dependencies": { 12161 - "@types/accepts": "^1.3.5", 12162 - "@types/body-parser": "1.19.2", 12163 - "@types/cors": "2.8.12", 12164 - "@types/express": "4.17.14", 12165 - "@types/express-serve-static-core": "4.17.31", 12166 - "accepts": "^1.3.5", 12167 - "apollo-server-core": "^3.13.0", 12168 - "apollo-server-types": "^3.8.0", 12169 - "body-parser": "^1.19.0", 12170 - "cors": "^2.8.5", 12171 - "parseurl": "^1.3.3" 12172 - }, 12173 - "engines": { 12174 - "node": ">=12.0" 12175 - }, 12176 - "peerDependencies": { 12177 - "express": "^4.17.1", 12178 - "graphql": "^15.3.0 || ^16.0.0" 12179 - } 12180 - }, 12181 - "node_modules/apollo-server-express/node_modules/@types/express-serve-static-core": { 12182 - "version": "4.17.31", 12183 - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", 12184 - "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", 12185 - "dependencies": { 12186 - "@types/node": "*", 12187 - "@types/qs": "*", 12188 - "@types/range-parser": "*" 12189 - } 12190 - }, 12191 - "node_modules/apollo-server-plugin-base": { 12192 - "version": "3.7.2", 12193 - "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-3.7.2.tgz", 12194 - "integrity": "sha512-wE8dwGDvBOGehSsPTRZ8P/33Jan6/PmL0y0aN/1Z5a5GcbFhDaaJCjK5cav6npbbGL2DPKK0r6MPXi3k3N45aw==", 12195 - "deprecated": "The `apollo-server-plugin-base` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12196 - "dependencies": { 12197 - "apollo-server-types": "^3.8.0" 12198 - }, 12199 - "engines": { 12200 - "node": ">=12.0" 12201 - }, 12202 - "peerDependencies": { 12203 - "graphql": "^15.3.0 || ^16.0.0" 12204 - } 12205 - }, 12206 - "node_modules/apollo-server-types": { 12207 - "version": "3.8.0", 12208 - "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-3.8.0.tgz", 12209 - "integrity": "sha512-ZI/8rTE4ww8BHktsVpb91Sdq7Cb71rdSkXELSwdSR0eXu600/sY+1UXhTWdiJvk+Eq5ljqoHLwLbY2+Clq2b9A==", 12210 - "deprecated": "The `apollo-server-types` package is part of Apollo Server v2 and v3, which are now deprecated (end-of-life October 22nd 2023). This package's functionality is now found in the `@apollo/server` package. See https://www.apollographql.com/docs/apollo-server/previous-versions/ for more details.", 12211 - "dependencies": { 12212 - "@apollo/utils.keyvaluecache": "^1.0.1", 12213 - "@apollo/utils.logger": "^1.0.0", 12214 - "apollo-reporting-protobuf": "^3.4.0", 12215 - "apollo-server-env": "^4.2.1" 12216 - }, 12217 - "engines": { 12218 - "node": ">=12.0" 12219 - }, 12220 - "peerDependencies": { 12221 - "graphql": "^15.3.0 || ^16.0.0" 12222 - } 12223 - }, 12224 12361 "node_modules/are-docs-informative": { 12225 12362 "version": "0.0.2", 12226 12363 "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", ··· 13247 13384 "node": ">=12.0.0" 13248 13385 } 13249 13386 }, 13387 + "node_modules/cross-inspect": { 13388 + "version": "1.0.1", 13389 + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", 13390 + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", 13391 + "license": "MIT", 13392 + "dependencies": { 13393 + "tslib": "^2.4.0" 13394 + }, 13395 + "engines": { 13396 + "node": ">=16.0.0" 13397 + } 13398 + }, 13250 13399 "node_modules/cross-spawn": { 13251 13400 "version": "7.0.6", 13252 13401 "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ··· 13273 13422 "version": "4.2.0", 13274 13423 "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", 13275 13424 "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" 13276 - }, 13277 - "node_modules/cssfilter": { 13278 - "version": "0.0.10", 13279 - "resolved": "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz", 13280 - "integrity": "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==" 13281 13425 }, 13282 13426 "node_modules/data-uri-to-buffer": { 13283 13427 "version": "4.0.1", ··· 15516 15660 "dev": true 15517 15661 }, 15518 15662 "node_modules/graphql": { 15519 - "version": "16.8.2", 15520 - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.2.tgz", 15521 - "integrity": "sha512-cvVIBILwuoSyD54U4cF/UXDh5yAobhNV/tPygI4lZhgOIJQE/WLWC4waBRb4I6bDVYb3OVx3lfHbaQOEoUD5sg==", 15663 + "version": "16.13.2", 15664 + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", 15665 + "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", 15666 + "license": "MIT", 15522 15667 "engines": { 15523 15668 "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" 15524 15669 } ··· 15592 15737 }, 15593 15738 "peerDependencies": { 15594 15739 "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" 15595 - } 15596 - }, 15597 - "node_modules/graphql-tag": { 15598 - "version": "2.12.6", 15599 - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", 15600 - "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", 15601 - "dependencies": { 15602 - "tslib": "^2.1.0" 15603 - }, 15604 - "engines": { 15605 - "node": ">=10" 15606 - }, 15607 - "peerDependencies": { 15608 - "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" 15609 15740 } 15610 15741 }, 15611 15742 "node_modules/gtoken": { ··· 17642 17773 "version": "0.4.1", 17643 17774 "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz", 17644 17775 "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==" 17645 - }, 17646 - "node_modules/lru-cache": { 17647 - "version": "7.13.1", 17648 - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz", 17649 - "integrity": "sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==", 17650 - "engines": { 17651 - "node": ">=12" 17652 - } 17653 17776 }, 17654 17777 "node_modules/luxon": { 17655 17778 "version": "3.7.2", ··· 21313 21436 "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 21314 21437 "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 21315 21438 }, 21316 - "node_modules/whatwg-mimetype": { 21317 - "version": "3.0.0", 21318 - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", 21319 - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", 21320 - "engines": { 21321 - "node": ">=12" 21322 - } 21323 - }, 21324 21439 "node_modules/whatwg-url": { 21325 21440 "version": "5.0.0", 21326 21441 "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", ··· 21588 21703 "engines": { 21589 21704 "node": ">=0.6.0" 21590 21705 } 21591 - }, 21592 - "node_modules/xss": { 21593 - "version": "1.0.14", 21594 - "resolved": "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz", 21595 - "integrity": "sha512-og7TEJhXvn1a7kzZGQ7ETjdQVS2UfZyTlsEdDOqvQF7GoxNfY+0YLCzBy1kPdsDDx4QuNAonQPddpsn6Xl/7sw==", 21596 - "dependencies": { 21597 - "commander": "^2.20.3", 21598 - "cssfilter": "0.0.10" 21599 - }, 21600 - "bin": { 21601 - "xss": "bin/xss" 21602 - }, 21603 - "engines": { 21604 - "node": ">= 0.10.0" 21605 - } 21606 - }, 21607 - "node_modules/xss/node_modules/commander": { 21608 - "version": "2.20.3", 21609 - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 21610 - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 21611 21706 }, 21612 21707 "node_modules/xtend": { 21613 21708 "version": "4.0.2",
+4 -7
server/package.json
··· 23 23 "author": "Roostorg", 24 24 "license": "ISC", 25 25 "dependencies": { 26 + "@apollo/server": "^5.5.0", 27 + "@as-integrations/express4": "^1.1.2", 26 28 "@aws-sdk/client-s3": "^3.1017.0", 27 29 "@aws-sdk/client-secrets-manager": "^3.1017.0", 28 30 "@aws-sdk/client-ses": "^3.1017.0", ··· 57 59 "@types/yargs": "^17.0.32", 58 60 "ajv": "^8.18.0", 59 61 "ajv-draft-04": "^1.0.0", 60 - "apollo-datasource": "^3.3.0", 61 - "apollo-server-core": "^3.11.1", 62 - "apollo-server-express": "^3.10.3", 63 62 "bcryptjs": "^2.4.3", 64 63 "bullmq": "^5.0.0", 65 64 "cassandra-driver": "^4.8.0", ··· 77 76 "formdata-node": "^6.0.3", 78 77 "fuzzball": "^2.1.2", 79 78 "generic-pool": "^3.8.2", 80 - "graphql": "^16.0.1", 79 + "graphql": "^16.13.2", 81 80 "graphql-depth-limit": "^1.1.0", 82 81 "graphql-passport": "^0.6.4", 83 82 "graphql-scalars": "^1.19.0", ··· 119 118 "@eslint/js": "^9.39.4", 120 119 "@faker-js/faker": "^7.5.0", 121 120 "@types/cls-hooked": "^4.3.3", 121 + "@types/cors": "^2.8.19", 122 122 "@types/graphql-depth-limit": "^1.1.6", 123 123 "@types/jest": "^29.2.4", 124 124 "@types/js-yaml": "^4.0.5", ··· 162 162 "@types/restify": "npm:pino@8.6.0", 163 163 "@googlemaps/google-maps-services-js@^3.3.16": { 164 164 "retry-axios": "npm:@ethanresnick/retry-axios@2.6.1" 165 - }, 166 - "apollo-server-express": { 167 - "@types/express": "npm:@types/express@4.17.17" 168 165 }, 169 166 "eslint-plugin-better-mutation": { 170 167 "lodash": "^4.18.1"