[CORL-878] Upgrade dependencies (#2867)

* chore: upgrade fluent

* chore: upgrade metascraper

* chore: upgrade akismet-api

* chore: upgrade apollo-server-express

* chore: upgrade archiver

* chore: upgrade bull

* chore: upgrade express, cheerio, content-security-policy-builder

* chore: upgrade convict

* chore: upgrade cors, cron

* chore: upgrade csv-stringify

* chore: upgrade dompurify

* chore: upgrade dotenv

* chore: upgrade express-static-gzip

* chore: upgrade fs-extra

* chore: upgrade graphql-js

* chore: upgrade graphql packages

* chore: upgrade html-minifier

* chore: upgrade html-to-text

* chore: upgrade ioredis

* chore: upgrade joi

* chore: upgrade jsdom

* chore: upgrade jsonwebtoken

* chore: upgrade juice

* chore: upgrade jwks-rsa ad linkifyjs

* chore: upgrade lodash

* chore: upgrade luxon

* chore: upgrade metascraper

* chore: upgrade mongodb

* chore: upgrade ms

* chore: upgrade node and node-fetch types

* chore: upgrade nodemailer nunjucks and typescript-eslint

* chore: Upgrade passport

* upgrade: prom-client react-helmet source-map-support stack-utils

* chore: upgrade uuid

* chore: upgrade @babel packages

* chore: upgrade types

* chore: upgrade autoprefixer

* chore: upgrade jest

* chore: upgrade ts-jest

* chore: remove linkify.d.ts

* chore: upgrade bowser

* chore: case-sensitive-paths-webpack-plugin

* chore: upgrade classnames

* chore: upgrade commander

* chore: upgrade comment-json

* chore: upgrade cross-spawn compression-webpack-plugin del

* chore: upgrade build and watch related dependencies

* chore: upgrade css-vars-ponyfill

* chore: upgrade eslint and css-vars-ponyfill

* chore: upgrade enzyme and eventemitter2

* fix: form bug

* chore: upgrade farce

* chore: upgrade final form

* chore: upgrade react-popper

* chore: upgrade flat and fork-ts-checker-webpack-plugin

* chore: upgrade husky and gulp related, intersection observer

* chore: upgrade lint-staged

* chore: upgrade marked, loader-utils, mini-css-extract-plugin

* chore: upgrade postcss-nested, proxy-polyfill, pstree.remy

* chore: upgrade prettier

* chore: fix prettier changes, upgrade react

* chore: mute createFactory deprecated message

* chore: upgrade react-copy-to-clipbard, react-axe, react-dom, react-test-renderer, react-timeago

* chore: upgrade react-transistion-group, react-responsive

* chore: upgrade types

* chore: upgrade react-dev-utils, react-error-overlay regenerator-runtime

* chore: upgrade types, sinon, sockjs-client, strip-ansi

* chore: upgrade types, fonts

* chore: upgrade nunjucks, ts-node, typescript-snapshot-plugin, wait-for-expect

* chore: upgrade eslint packages

* chore: upgrade fluent, types

* chore: upgrade jsdom dep

* chore: upgrade mongo

* chore: upgrade deps

* chore: upgrade typescript, recompose

* chore: upgrade prettier

* chore: remove obsolete prettier config

* chore: upgrade jsdom types

* chore: upgrade typescript-eslint

* chore: upgrad deps

* chore: upgrade deps

* chore: upgrade relay related modules

* chore: upgrade docz WIP

* chore: upgrade docz

* chore: add guard

* chore: remove obsolete line

* chore: comment

* chore: refactors

* fix: hook count change error
This commit is contained in:
Vinh
2020-04-15 18:15:31 +02:00
committed by GitHub
parent e180cf87d7
commit c9a0ab8848
996 changed files with 29214 additions and 18254 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import { RedisPubSub } from "graphql-redis-subscriptions";
import { Db } from "mongodb";
import uuid from "uuid";
import { v1 as uuid } from "uuid";
import { LanguageCode } from "coral-common/helpers/i18n/locales";
import { Config } from "coral-server/config";
@@ -81,7 +81,7 @@ export default class GraphContext {
public readonly user?: User;
constructor(options: GraphContextOptions) {
this.id = options.id || uuid.v1();
this.id = options.id || uuid();
this.now = options.now || new Date();
this.lang = options.lang || options.i18n.getDefaultLang();
this.disableCaching = options.disableCaching || false;
+1 -1
View File
@@ -81,7 +81,7 @@ const auth: DirectiveResolverFn<
(!permit && conditions.length > 0) ||
// If the permit was specified, and some of the conditions for the user
// aren't in the list of permitted conditions, then error.
(permit && conditions.some(condition => !permit.includes(condition)))
(permit && conditions.some((condition) => !permit.includes(condition)))
) {
// Compute the resource that the user was attempting to access.
const resource = calculateLocationKey(info);
+2 -8
View File
@@ -51,15 +51,9 @@ const rate: DirectiveResolverFn<
const key = `${tenant.id}:rl:${user.id}:${info.operation.operation}.${resource}`;
// Perform the rate limiting check.
const [[, tries]] = await redis
.multi()
.incr(key)
.expire(key, seconds)
.exec();
const [[, tries]] = await redis.multi().incr(key).expire(key, seconds).exec();
if (tries && tries > max) {
const resetsAt = DateTime.fromJSDate(now)
.plus({ seconds })
.toJSDate();
const resetsAt = DateTime.fromJSDate(now).plus({ seconds }).toJSDate();
throw new RateLimitExceeded(key, max, resetsAt, tries);
}
+1 -1
View File
@@ -24,7 +24,7 @@ export async function mapFieldsetToErrorCodes<T>(
continue;
}
if (errorMap[param].some(code => err.code === code)) {
if (errorMap[param].some((code) => err.code === code)) {
err.param = param;
break;
}
@@ -1,5 +1,5 @@
import { ApolloError } from "apollo-server-core";
import { GraphQLError } from "graphql";
import { GraphQLFormattedError } from "graphql";
import { GraphQLExtension, GraphQLResponse } from "graphql-extensions";
import { merge } from "lodash";
@@ -9,10 +9,14 @@ import {
InternalError,
} from "coral-server/errors";
import GraphContext from "coral-server/graph/context";
import { getOriginalError } from "./helpers";
function hoistCoralErrorExtensions(ctx: GraphContext, err: GraphQLError): void {
function hoistCoralErrorExtensions(
ctx: GraphContext,
err: GraphQLFormattedError
): void {
// Grab or wrap the originalError so that it's a CoralError.
const originalError = extractOriginalError(err, ctx);
const originalError = getWrappedOriginalError(err, ctx);
if (!originalError) {
return;
}
@@ -25,7 +29,7 @@ function hoistCoralErrorExtensions(ctx: GraphContext, err: GraphQLError): void {
// Hoist the message from the original error into the message of the base
// error.
err.message = extensions.message;
(err as any).message = extensions.message;
// Re-hoist the extensions.
merge(err.extensions, extensions);
@@ -39,8 +43,8 @@ function hoistCoralErrorExtensions(ctx: GraphContext, err: GraphQLError): void {
* @param err the error to have their original error extracted from.
* @param ctx the Context to extract the environment state.
*/
function extractOriginalError(
err: GraphQLError,
function getWrappedOriginalError(
err: GraphQLFormattedError,
ctx: GraphContext
): CoralError | undefined {
if (err instanceof ApolloError) {
@@ -49,23 +53,25 @@ function extractOriginalError(
return;
}
if (!err.originalError) {
const originalError = getOriginalError(err);
if (!originalError) {
// Only errors that have an originalError need to be hoisted.
return;
}
if (err.originalError instanceof CoralError) {
return err.originalError;
if (originalError instanceof CoralError) {
return originalError;
}
if (ctx.config.get("env") !== "production") {
return new InternalDevelopmentError(
err.originalError,
originalError,
"wrapped internal development error"
);
}
return new InternalError(err.originalError, "wrapped internal error");
return new InternalError(originalError, "wrapped internal error");
}
/**
@@ -76,8 +82,8 @@ function extractOriginalError(
*/
export function enrichError(
ctx: GraphContext,
err: GraphQLError
): GraphQLError {
err: GraphQLFormattedError
): GraphQLFormattedError {
if (err.extensions) {
// Delete the exception field from the error extension, we never need to
// provide that data.
@@ -85,7 +91,7 @@ export function enrichError(
delete err.extensions.exception;
}
if (err.originalError) {
if (getOriginalError(err)) {
// Hoist the extensions onto the error.
hoistCoralErrorExtensions(ctx, err);
}
@@ -104,7 +110,7 @@ export class ErrorWrappingExtension implements GraphQLExtension<GraphContext> {
...o,
graphqlResponse: {
...o.graphqlResponse,
errors: o.graphqlResponse.errors.map(err =>
errors: o.graphqlResponse.errors.map((err) =>
enrichError(o.context, err)
),
},
@@ -1,4 +1,4 @@
import { DocumentNode, ExecutionArgs, GraphQLError } from "graphql";
import { DocumentNode, ExecutionArgs, GraphQLFormattedError } from "graphql";
import {
EndHandler,
GraphQLExtension,
@@ -11,7 +11,7 @@ import logger from "coral-server/logger";
import { getOperationMetadata, getPersistedQueryMetadata } from "./helpers";
export function logError(ctx: GraphContext, err: GraphQLError) {
export function logError(ctx: GraphContext, err: GraphQLFormattedError) {
ctx.logger.error({ err }, "graphql query error");
}
@@ -102,7 +102,7 @@ export class LoggerExtension implements GraphQLExtension<GraphContext> {
context: GraphContext;
}): void {
if (response.graphqlResponse.errors) {
response.graphqlResponse.errors.forEach(err =>
response.graphqlResponse.errors.forEach((err) =>
logError(response.context, err)
);
}
@@ -1,5 +1,6 @@
import {
DocumentNode,
GraphQLFormattedError,
OperationDefinitionNode,
OperationTypeNode,
} from "graphql";
@@ -66,3 +67,16 @@ export const getPersistedQueryMetadata = ({
operation,
operationName,
});
/**
* getOriginalError tries to return the original error from a
* formatted GraphQL error.
*
* @param err A GraphQL Formatted Error
*/
export const getOriginalError = (err: GraphQLFormattedError) => {
if ((err as any).originalError) {
return (err as any).originalError as Error;
}
return null;
};
+2 -2
View File
@@ -11,8 +11,8 @@ export default (ctx: GraphContext) => ({
string,
GQLDiscoveredOIDCConfiguration | null
>(
issuers =>
Promise.all(issuers.map(issuer => discoverOIDCConfiguration(issuer))),
(issuers) =>
Promise.all(issuers.map((issuer) => discoverOIDCConfiguration(issuer))),
{
// Disable caching for the DataLoader if the Context is designed to be
// long lived.
+1 -1
View File
@@ -68,7 +68,7 @@ const primeCommentsFromConnection = (ctx: Context) => (
) => {
if (!ctx.disableCaching) {
// For each of the nodes, prime the comment loader.
connection.nodes.forEach(comment => {
connection.nodes.forEach((comment) => {
ctx.loaders.Comments.visible.prime(comment.id, comment);
});
}
+1 -1
View File
@@ -12,7 +12,7 @@ import { QueryToSitesArgs } from "coral-server/graph/schema/__generated__/types"
export default (ctx: TenantContext) => ({
site: new DataLoader<string, Site | null>(
ids => retrieveManySites(ctx.mongo, ctx.tenant.id, ids),
(ids) => retrieveManySites(ctx.mongo, ctx.tenant.id, ids),
{
cache: !ctx.disableCaching,
}
+2 -2
View File
@@ -145,7 +145,7 @@ const primeStoriesFromConnection = (ctx: GraphContext) => (
) => {
if (!ctx.disableCaching) {
// For each of these nodes, prime the story loader.
connection.nodes.forEach(story => {
connection.nodes.forEach((story) => {
ctx.loaders.Stories.story.prime(story.id, story);
});
}
@@ -186,7 +186,7 @@ export default (ctx: GraphContext) => ({
}
),
story: new DataLoader<string, Story | null>(
ids => retrieveManyStories(ctx.mongo, ctx.tenant.id, ids),
(ids) => retrieveManyStories(ctx.mongo, ctx.tenant.id, ids),
{
// Disable caching for the DataLoader if the Context is designed to be
// long lived.
+2 -2
View File
@@ -88,7 +88,7 @@ const primeUsersFromConnection = (ctx: Context) => (
) => {
if (!ctx.disableCaching) {
// For each of the nodes, prime the user loader.
connection.nodes.forEach(user => {
connection.nodes.forEach((user) => {
ctx.loaders.Users.user.prime(user.id, user);
});
}
@@ -98,7 +98,7 @@ const primeUsersFromConnection = (ctx: Context) => (
export default (ctx: Context) => {
const user = new DataLoader<string, User | null>(
ids => retrieveManyUsers(ctx.mongo, ctx.tenant.id, ids),
(ids) => retrieveManyUsers(ctx.mongo, ctx.tenant.id, ids),
{
// Disable caching for the DataLoader if the Context is designed to be
// long lived.
+3 -2
View File
@@ -27,7 +27,7 @@ export class SingletonResolver<T> {
return this.cache;
}
const promise = this.resolver().then(result => {
const promise = this.resolver().then((result) => {
return result;
});
@@ -41,5 +41,6 @@ export class SingletonResolver<T> {
export function createManyBatchLoadFn<U, V>(
batchLoadFn: (input: U) => Promise<V>
) {
return (inputs: U[]) => Promise.all(inputs.map(input => batchLoadFn(input)));
return (inputs: U[]) =>
Promise.all(inputs.map((input) => batchLoadFn(input)));
}
+2 -2
View File
@@ -168,7 +168,7 @@ export const Comments = (ctx: GraphContext) => ({
GQLTAG.FEATURED,
ctx.now
)
.then(comment =>
.then((comment) =>
comment.status !== GQLCOMMENT_STATUS.APPROVED
? approveComment(
ctx.mongo,
@@ -182,7 +182,7 @@ export const Comments = (ctx: GraphContext) => ({
)
: comment
)
.then(comment => {
.then((comment) => {
// Publish that the comment was featured.
publishCommentFeatured(ctx.broker, comment);
-2
View File
@@ -1,5 +1,3 @@
import { Omit } from "coral-common/types";
/**
* validateMaximumLength will limit the value of an optional string to the
* specified amount, otherwise will throw an error.
@@ -5,12 +5,10 @@ import {
const disabled = { enabled: false };
export const AuthIntegrations: GQLAuthIntegrationsTypeResolver<
GQLAuthIntegrations
> = {
local: auth => auth.local || disabled,
sso: auth => auth.sso || disabled,
oidc: auth => auth.oidc || disabled,
google: auth => auth.google || disabled,
facebook: auth => auth.facebook || disabled,
export const AuthIntegrations: GQLAuthIntegrationsTypeResolver<GQLAuthIntegrations> = {
local: (auth) => auth.local || disabled,
sso: (auth) => auth.sso || disabled,
oidc: (auth) => auth.oidc || disabled,
google: (auth) => auth.google || disabled,
facebook: (auth) => auth.facebook || disabled,
};
+1 -1
View File
@@ -8,5 +8,5 @@ export type BanStatusInput = user.ConsolidatedBanStatus & {
export const BanStatus: Required<GQLBanStatusTypeResolver<BanStatusInput>> = {
active: ({ active }) => active,
history: ({ history, userID }) =>
history.map(status => ({ ...status, userID })),
history.map((status) => ({ ...status, userID })),
};
@@ -1,9 +1,9 @@
import { GQLBanStatusHistoryTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
export const BanStatusHistory: Required<
GQLBanStatusHistoryTypeResolver<user.BanStatusHistory>
> = {
export const BanStatusHistory: Required<GQLBanStatusHistoryTypeResolver<
user.BanStatusHistory
>> = {
active: ({ active }) => active,
createdBy: ({ createdBy }, input, ctx) => {
if (createdBy) {
@@ -2,9 +2,7 @@ import { GQLCloseCommentingTypeResolver } from "coral-server/graph/schema/__gene
import * as settings from "coral-server/models/settings";
import { translate } from "coral-server/services/i18n";
export const CloseCommenting: GQLCloseCommentingTypeResolver<
settings.CloseCommenting
> = {
export const CloseCommenting: GQLCloseCommentingTypeResolver<settings.CloseCommenting> = {
message: (closeCommenting, input, ctx) => {
if (closeCommenting.message) {
return closeCommenting.message;
+7 -6
View File
@@ -44,15 +44,16 @@ export const maybeLoadOnlyID = (
};
export const Comment: GQLCommentTypeResolver<comment.Comment> = {
body: c => (c.revisions.length > 0 ? getLatestRevision(c).body : null),
body: (c) => (c.revisions.length > 0 ? getLatestRevision(c).body : null),
// Send the whole comment back when you request revisions. This way, we get to
// know the comment ID. The field mapping is handled by the CommentRevision
// resolver.
revision: c =>
revision: (c) =>
c.revisions.length > 0
? { revision: getLatestRevision(c), comment: c }
: null,
revisionHistory: c => c.revisions.map(revision => ({ revision, comment: c })),
revisionHistory: (c) =>
c.revisions.map((revision) => ({ revision, comment: c })),
editing: ({ revisions, createdAt }, input, ctx) => ({
// When there is more than one body history, then the comment has been
// edited.
@@ -84,7 +85,7 @@ export const Comment: GQLCommentTypeResolver<comment.Comment> = {
);
},
// Action Counts are encoded, decode them for use with the GraphQL system.
actionCounts: c => decodeActionCounts(c.actionCounts),
actionCounts: (c) => decodeActionCounts(c.actionCounts),
flags: ({ id }, { first, after }, ctx) =>
ctx.loaders.CommentActions.connection({
first: defaultTo(first, 10),
@@ -96,8 +97,8 @@ export const Comment: GQLCommentTypeResolver<comment.Comment> = {
}),
viewerActionPresence: (c, input, ctx) =>
ctx.user ? ctx.loaders.Comments.retrieveMyActionPresence.load(c.id) : null,
parentCount: c => (hasAncestors(c) ? c.ancestorIDs.length : 0),
depth: c => (hasAncestors(c) ? c.ancestorIDs.length : 0),
parentCount: (c) => (hasAncestors(c) ? c.ancestorIDs.length : 0),
depth: (c) => (hasAncestors(c) ? c.ancestorIDs.length : 0),
rootParent: (c, input, ctx, info) =>
hasAncestors(c)
? maybeLoadOnlyID(ctx, info, c.ancestorIDs[c.ancestorIDs.length - 1])
@@ -3,9 +3,7 @@ import { GQLCommentCreatedPayloadTypeResolver } from "coral-server/graph/schema/
import { maybeLoadOnlyID } from "./Comment";
import { CommentCreatedInput } from "./Subscription/commentCreated";
export const CommentCreatedPayload: GQLCommentCreatedPayloadTypeResolver<
CommentCreatedInput
> = {
export const CommentCreatedPayload: GQLCommentCreatedPayloadTypeResolver<CommentCreatedInput> = {
comment: ({ commentID }, args, ctx, info) =>
maybeLoadOnlyID(ctx, info, commentID),
};
@@ -3,9 +3,7 @@ import { GQLCommentEnteredModerationQueuePayloadTypeResolver } from "coral-serve
import { maybeLoadOnlyID } from "./Comment";
import { CommentEnteredModerationQueueInput } from "./Subscription/commentEnteredModerationQueue";
export const CommentEnteredModerationQueuePayload: GQLCommentEnteredModerationQueuePayloadTypeResolver<
CommentEnteredModerationQueueInput
> = {
export const CommentEnteredModerationQueuePayload: GQLCommentEnteredModerationQueuePayloadTypeResolver<CommentEnteredModerationQueueInput> = {
comment: ({ commentID }, args, ctx, info) =>
maybeLoadOnlyID(ctx, info, commentID),
};
@@ -3,9 +3,7 @@ import { GQLCommentLeftModerationQueuePayloadTypeResolver } from "coral-server/g
import { maybeLoadOnlyID } from "./Comment";
import { CommentLeftModerationQueueInput } from "./Subscription/commentLeftModerationQueue";
export const CommentLeftModerationQueuePayload: GQLCommentLeftModerationQueuePayloadTypeResolver<
CommentLeftModerationQueueInput
> = {
export const CommentLeftModerationQueuePayload: GQLCommentLeftModerationQueuePayloadTypeResolver<CommentLeftModerationQueueInput> = {
comment: ({ commentID }, args, ctx, info) =>
maybeLoadOnlyID(ctx, info, commentID),
};
@@ -2,9 +2,7 @@ import * as actions from "coral-server/models/action/moderation/comment";
import { GQLCommentModerationActionTypeResolver } from "../schema/__generated__/types";
export const CommentModerationAction: GQLCommentModerationActionTypeResolver<
actions.CommentModerationAction
> = {
export const CommentModerationAction: GQLCommentModerationActionTypeResolver<actions.CommentModerationAction> = {
revision: async (action, input, ctx) => {
const comment = await ctx.loaders.Comments.visible.load(action.commentID);
if (!comment) {
@@ -3,9 +3,7 @@ import { GQLCommentReleasedPayloadTypeResolver } from "coral-server/graph/schema
import { maybeLoadOnlyID } from "./Comment";
import { CommentReleasedInput } from "./Subscription/commentReleased";
export const CommentReleasedPayload: GQLCommentReleasedPayloadTypeResolver<
CommentReleasedInput
> = {
export const CommentReleasedPayload: GQLCommentReleasedPayloadTypeResolver<CommentReleasedInput> = {
comment: ({ commentID }, args, ctx, info) =>
maybeLoadOnlyID(ctx, info, commentID),
};
@@ -3,9 +3,7 @@ import { GQLCommentReplyCreatedPayloadTypeResolver } from "coral-server/graph/sc
import { maybeLoadOnlyID } from "./Comment";
import { CommentReplyCreatedInput } from "./Subscription/commentReplyCreated";
export const CommentReplyCreatedPayload: GQLCommentReplyCreatedPayloadTypeResolver<
CommentReplyCreatedInput
> = {
export const CommentReplyCreatedPayload: GQLCommentReplyCreatedPayloadTypeResolver<CommentReplyCreatedInput> = {
comment: ({ commentID }, args, ctx, info) =>
maybeLoadOnlyID(ctx, info, commentID),
};
@@ -7,14 +7,14 @@ export interface WrappedCommentRevision {
comment: Comment;
}
export const CommentRevision: Required<
GQLCommentRevisionTypeResolver<WrappedCommentRevision>
> = {
id: w => w.revision.id,
comment: w => w.comment,
actionCounts: w => decodeActionCounts(w.revision.actionCounts),
body: w => w.revision.body,
export const CommentRevision: Required<GQLCommentRevisionTypeResolver<
WrappedCommentRevision
>> = {
id: (w) => w.revision.id,
comment: (w) => w.comment,
actionCounts: (w) => decodeActionCounts(w.revision.actionCounts),
body: (w) => w.revision.body,
// Defaults to an empty object if not set on the revision.
metadata: w => w.revision.metadata || {},
createdAt: w => w.revision.createdAt,
metadata: (w) => w.revision.metadata || {},
createdAt: (w) => w.revision.createdAt,
};
@@ -3,9 +3,7 @@ import { GQLCommentStatusUpdatedPayloadTypeResolver } from "coral-server/graph/s
import { maybeLoadOnlyID } from "./Comment";
import { CommentStatusUpdatedInput } from "./Subscription/commentStatusUpdated";
export const CommentStatusUpdatedPayload: GQLCommentStatusUpdatedPayloadTypeResolver<
CommentStatusUpdatedInput
> = {
export const CommentStatusUpdatedPayload: GQLCommentStatusUpdatedPayloadTypeResolver<CommentStatusUpdatedInput> = {
moderator: ({ moderatorID }, args, ctx) =>
moderatorID ? ctx.loaders.Users.user.load(moderatorID) : null,
comment: ({ commentID }, args, ctx, info) =>
@@ -2,9 +2,7 @@ import { GQLDisableCommentingTypeResolver } from "coral-server/graph/schema/__ge
import * as settings from "coral-server/models/settings";
import { translate } from "coral-server/services/i18n";
export const DisableCommenting: GQLDisableCommentingTypeResolver<
settings.DisableCommenting
> = {
export const DisableCommenting: GQLDisableCommentingTypeResolver<settings.DisableCommenting> = {
message: (disableCommenting, input, ctx) => {
if (disableCommenting.message) {
return disableCommenting.message;
@@ -5,9 +5,7 @@ import {
import { reconstructTenantURLResolver } from "./util";
export const FacebookAuthIntegration: GQLFacebookAuthIntegrationTypeResolver<
GQLFacebookAuthIntegration
> = {
export const FacebookAuthIntegration: GQLFacebookAuthIntegrationTypeResolver<GQLFacebookAuthIntegration> = {
callbackURL: reconstructTenantURLResolver("/api/auth/facebook/callback"),
redirectURL: reconstructTenantURLResolver("/api/auth/facebook"),
};
@@ -5,9 +5,7 @@ import {
import { reconstructTenantURLResolver } from "./util";
export const GoogleAuthIntegration: GQLGoogleAuthIntegrationTypeResolver<
GQLGoogleAuthIntegration
> = {
export const GoogleAuthIntegration: GQLGoogleAuthIntegrationTypeResolver<GQLGoogleAuthIntegration> = {
callbackURL: reconstructTenantURLResolver("/api/auth/google/callback"),
redirectURL: reconstructTenantURLResolver("/api/auth/google"),
};
@@ -5,9 +5,7 @@ import * as settings from "coral-server/models/settings";
export type LiveConfigurationInput = settings.LiveConfiguration;
export const LiveConfiguration: GQLLiveConfigurationTypeResolver<
LiveConfigurationInput
> = {
export const LiveConfiguration: GQLLiveConfigurationTypeResolver<LiveConfigurationInput> = {
configurable: (source, args, ctx) =>
Boolean(!ctx.config.get("disable_live_updates")),
enabled: (source, args, ctx) => {
@@ -15,9 +15,7 @@ export interface ModerationQueueInput {
count: number | null;
}
export const ModerationQueue: GQLModerationQueueTypeResolver<
ModerationQueueInput
> = {
export const ModerationQueue: GQLModerationQueueTypeResolver<ModerationQueueInput> = {
id: ({ selector, connection: { filter } }) => {
// NOTE: (wyattjoh) when the queues change shape in the future, investigate adding more dynamicness to this id generation
if (filter && filter.storyID) {
@@ -134,9 +134,7 @@ export const moderationQueuesResolver: QueryToModerationQueuesResolver = async (
return sharedModerationInputResolver(source, args, ctx);
};
export const ModerationQueues: GQLModerationQueuesTypeResolver<
ModerationQueuesInput
> = {
export const ModerationQueues: GQLModerationQueuesTypeResolver<ModerationQueuesInput> = {
unmoderated: mergeModerationInputFilters(
{
status: { $in: UNMODERATED_STATUSES },
@@ -1,9 +1,9 @@
import { GQLModeratorNoteTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
export const ModeratorNote: Required<
GQLModeratorNoteTypeResolver<user.ModeratorNote>
> = {
export const ModeratorNote: Required<GQLModeratorNoteTypeResolver<
user.ModeratorNote
>> = {
createdBy: ({ createdBy }, input, ctx) => {
return ctx.loaders.Users.user.load(createdBy);
},
@@ -5,9 +5,7 @@ import {
import { reconstructTenantURLResolver } from "./util";
export const OIDCAuthIntegration: GQLOIDCAuthIntegrationTypeResolver<
GQLOIDCAuthIntegration
> = {
export const OIDCAuthIntegration: GQLOIDCAuthIntegrationTypeResolver<GQLOIDCAuthIntegration> = {
callbackURL: reconstructTenantURLResolver("/api/auth/oidc/callback"),
redirectURL: reconstructTenantURLResolver("/api/auth/oidc"),
};
@@ -5,10 +5,10 @@ export type PremodStatusInput = user.ConsolidatedPremodStatus & {
userID: string;
};
export const PremodStatus: Required<
GQLPremodStatusTypeResolver<PremodStatusInput>
> = {
export const PremodStatus: Required<GQLPremodStatusTypeResolver<
PremodStatusInput
>> = {
active: ({ active }) => active,
history: ({ history, userID }) =>
history.map(status => ({ ...status, userID })),
history.map((status) => ({ ...status, userID })),
};
@@ -1,9 +1,9 @@
import { GQLPremodStatusHistoryTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
export const PremodStatusHistory: Required<
GQLPremodStatusHistoryTypeResolver<user.PremodStatusHistory>
> = {
export const PremodStatusHistory: Required<GQLPremodStatusHistoryTypeResolver<
user.PremodStatusHistory
>> = {
active: ({ active }) => active,
createdBy: ({ createdBy }, input, ctx) => {
if (createdBy) {
+1 -1
View File
@@ -1,7 +1,7 @@
import { GQLProfileTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
const resolveType: GQLProfileTypeResolver<user.Profile> = profile => {
const resolveType: GQLProfileTypeResolver<user.Profile> = (profile) => {
switch (profile.type) {
case "local":
return "LocalProfile";
+1 -1
View File
@@ -8,5 +8,5 @@ export interface QueueInput {
}
export const Queue: Required<GQLQueueTypeResolver<QueueInput>> = {
counts: t => t.counts(),
counts: (t) => t.counts(),
};
+5 -5
View File
@@ -15,9 +15,9 @@ const get = (fn: (ctx: GraphContext) => QueueInput) => (
) => fn(ctx);
export const Queues: Required<GQLQueuesTypeResolver> = {
mailer: get(ctx => ctx.mailerQueue),
scraper: get(ctx => ctx.scraperQueue),
notifier: get(ctx => ctx.notifierQueue),
webhook: get(ctx => ctx.webhookQueue),
rejector: get(ctx => ctx.rejectorQueue),
mailer: get((ctx) => ctx.mailerQueue),
scraper: get((ctx) => ctx.scraperQueue),
notifier: get((ctx) => ctx.notifierQueue),
webhook: get((ctx) => ctx.webhookQueue),
rejector: get((ctx) => ctx.rejectorQueue),
};
@@ -4,9 +4,9 @@ export interface RecentCommentHistoryInput {
userID: string;
}
export const RecentCommentHistory: Required<
GQLRecentCommentHistoryTypeResolver<RecentCommentHistoryInput>
> = {
export const RecentCommentHistory: Required<GQLRecentCommentHistoryTypeResolver<
RecentCommentHistoryInput
>> = {
statuses: ({ userID }, args, ctx) =>
ctx.loaders.Comments.authorStatusCounts.load(userID),
};
@@ -4,12 +4,10 @@ import { GQLSSOAuthIntegrationTypeResolver } from "coral-server/graph/schema/__g
function getActiveSSOKey(keys: settings.Secret[]) {
// Any key that has been rotated cannot be the active key.
return keys.find(key => !key.rotatedAt);
return keys.find((key) => !key.rotatedAt);
}
export const SSOAuthIntegration: GQLSSOAuthIntegrationTypeResolver<
settings.SSOAuthIntegration
> = {
export const SSOAuthIntegration: GQLSSOAuthIntegrationTypeResolver<settings.SSOAuthIntegration> = {
key: ({ keys }) => {
const key = getActiveSSOKey(keys);
if (key) {
@@ -3,9 +3,7 @@ import {
GQLSlackConfigurationTypeResolver,
} from "coral-server/graph/schema/__generated__/types";
export const SlackConfiguration: GQLSlackConfigurationTypeResolver<
GQLSlackConfiguration
> = {
export const SlackConfiguration: GQLSlackConfigurationTypeResolver<GQLSlackConfiguration> = {
// TODO: Remove this when we create a migration to generate slack channels on existing tenants
channels: ({ channels = [] }) => channels,
};
+1 -1
View File
@@ -22,7 +22,7 @@ export const Story: GQLStoryTypeResolver<story.Story> = {
: GQLSTORY_STATUS.OPEN,
isClosed: (s, input, ctx) => story.isStoryClosed(ctx.tenant, s, ctx.now),
closedAt: (s, input, ctx) => story.getStoryClosedAt(ctx.tenant, s) || null,
commentActionCounts: s => decodeActionCounts(s.commentCounts.action),
commentActionCounts: (s) => decodeActionCounts(s.commentCounts.action),
commentCounts: (s): CommentCountsInput => s,
// Merge tenant settings into the story settings so we can easily inherit the
// options if they exist.
@@ -7,14 +7,12 @@ import {
GQLStorySettingsTypeResolver,
} from "../schema/__generated__/types";
export const StorySettings: GQLStorySettingsTypeResolver<
story.StorySettings
> = {
live: s => s.live || {},
export const StorySettings: GQLStorySettingsTypeResolver<story.StorySettings> = {
live: (s) => s.live || {},
moderation: (s, input, ctx) => s.moderation || ctx.tenant.moderation,
premodLinksEnable: (s, input, ctx) =>
s.premodLinksEnable || ctx.tenant.premodLinksEnable,
messageBox: s => {
messageBox: (s) => {
if (s.messageBox) {
return s.messageBox;
}
@@ -17,14 +17,15 @@ export type CommentCreatedSubscription = SubscriptionType<
CommentCreatedInput
>;
export const commentCreated: SubscriptionToCommentCreatedResolver<
CommentCreatedInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_CREATED, {
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
export const commentCreated: SubscriptionToCommentCreatedResolver<CommentCreatedInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_CREATED,
{
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -22,22 +22,23 @@ export type CommentEnteredModerationQueueSubscription = SubscriptionType<
CommentEnteredModerationQueueInput
>;
export const commentEnteredModerationQueue: SubscriptionToCommentEnteredModerationQueueResolver<
CommentEnteredModerationQueueInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_ENTERED_MODERATION_QUEUE, {
filter: (source, { storyID, queue }) => {
// If we're filtering by storyID, then only send back comments with the
// specific storyID.
if (storyID && source.storyID !== storyID) {
return false;
}
export const commentEnteredModerationQueue: SubscriptionToCommentEnteredModerationQueueResolver<CommentEnteredModerationQueueInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_ENTERED_MODERATION_QUEUE,
{
filter: (source, { storyID, queue }) => {
// If we're filtering by storyID, then only send back comments with the
// specific storyID.
if (storyID && source.storyID !== storyID) {
return false;
}
// If we're filtering by queue, then only send back comments from the
// specific queue.
if (queue && source.queue !== queue) {
return false;
}
// If we're filtering by queue, then only send back comments from the
// specific queue.
if (queue && source.queue !== queue) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -17,14 +17,15 @@ export type CommentFeaturedSubscription = SubscriptionType<
CommentFeaturedInput
>;
export const commentFeatured: SubscriptionToCommentFeaturedResolver<
CommentFeaturedInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_FEATURED, {
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
export const commentFeatured: SubscriptionToCommentFeaturedResolver<CommentFeaturedInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_FEATURED,
{
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -21,22 +21,23 @@ export type CommentLeftModerationQueueSubscription = SubscriptionType<
CommentLeftModerationQueueInput
>;
export const commentLeftModerationQueue: SubscriptionToCommentLeftModerationQueueResolver<
CommentLeftModerationQueueInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_LEFT_MODERATION_QUEUE, {
filter: (source, { storyID, queue }) => {
// If we're filtering by storyID, then only send back comments with the
// specific storyID.
if (storyID && source.storyID !== storyID) {
return false;
}
export const commentLeftModerationQueue: SubscriptionToCommentLeftModerationQueueResolver<CommentLeftModerationQueueInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_LEFT_MODERATION_QUEUE,
{
filter: (source, { storyID, queue }) => {
// If we're filtering by storyID, then only send back comments with the
// specific storyID.
if (storyID && source.storyID !== storyID) {
return false;
}
// If we're filtering by queue, then only send back comments from the
// specific queue.
if (queue && source.queue !== queue) {
return false;
}
// If we're filtering by queue, then only send back comments from the
// specific queue.
if (queue && source.queue !== queue) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -17,14 +17,15 @@ export type CommentReleasedSubscription = SubscriptionType<
CommentReleasedInput
>;
export const commentReleased: SubscriptionToCommentReleasedResolver<
CommentReleasedInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_RELEASED, {
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
export const commentReleased: SubscriptionToCommentReleasedResolver<CommentReleasedInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_RELEASED,
{
filter: (source, { storyID }) => {
if (source.storyID !== storyID) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -17,14 +17,15 @@ export type CommentReplyCreatedSubscription = SubscriptionType<
CommentReplyCreatedInput
>;
export const commentReplyCreated: SubscriptionToCommentReplyCreatedResolver<
CommentReplyCreatedInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_REPLY_CREATED, {
filter: (source, { ancestorID }) => {
if (!source.ancestorIDs.includes(ancestorID)) {
return false;
}
export const commentReplyCreated: SubscriptionToCommentReplyCreatedResolver<CommentReplyCreatedInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_REPLY_CREATED,
{
filter: (source, { ancestorID }) => {
if (!source.ancestorIDs.includes(ancestorID)) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -23,16 +23,17 @@ export type CommentStatusUpdatedSubscription = SubscriptionType<
CommentStatusUpdatedInput
>;
export const commentStatusUpdated: SubscriptionToCommentStatusUpdatedResolver<
CommentStatusUpdatedInput
> = createIterator(SUBSCRIPTION_CHANNELS.COMMENT_STATUS_UPDATED, {
filter: (source, { id }) => {
// If we're filtering by id, then only send back updates for the specified
// comment.
if (id && source.commentID !== id) {
return false;
}
export const commentStatusUpdated: SubscriptionToCommentStatusUpdatedResolver<CommentStatusUpdatedInput> = createIterator(
SUBSCRIPTION_CHANNELS.COMMENT_STATUS_UPDATED,
{
filter: (source, { id }) => {
// If we're filtering by id, then only send back updates for the specified
// comment.
if (id && source.commentID !== id) {
return false;
}
return true;
},
});
return true;
},
}
);
@@ -102,6 +102,6 @@ export function createIterator<
createTenantAsyncIterator(channel),
createFilterFn(filter)
),
resolve: payload => payload,
resolve: (payload) => payload,
};
}
@@ -20,9 +20,7 @@ export const Subscription: GQLSubscriptionTypeResolver = {
export { CommentFeaturedInput } from "./commentFeatured";
export { CommentCreatedInput } from "./commentCreated";
export {
CommentEnteredModerationQueueInput,
} from "./commentEnteredModerationQueue";
export { CommentEnteredModerationQueueInput } from "./commentEnteredModerationQueue";
export { CommentLeftModerationQueueInput } from "./commentLeftModerationQueue";
export { CommentReleasedInput } from "./commentReleased";
export { CommentReplyCreatedInput } from "./commentReplyCreated";
@@ -5,11 +5,11 @@ export type SuspensionStatusInput = user.ConsolidatedSuspensionStatus & {
userID: string;
};
export const SuspensionStatus: Required<
GQLSuspensionStatusTypeResolver<SuspensionStatusInput>
> = {
export const SuspensionStatus: Required<GQLSuspensionStatusTypeResolver<
SuspensionStatusInput
>> = {
active: ({ active }) => active,
until: ({ until }) => until,
history: ({ history, userID }) =>
history.map(status => ({ ...status, userID })),
history.map((status) => ({ ...status, userID })),
};
@@ -1,9 +1,9 @@
import { GQLSuspensionStatusHistoryTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
export const SuspensionStatusHistory: Required<
GQLSuspensionStatusHistoryTypeResolver<user.SuspensionStatusHistory>
> = {
export const SuspensionStatusHistory: Required<GQLSuspensionStatusHistoryTypeResolver<
user.SuspensionStatusHistory
>> = {
active: ({ from }, input, ctx) =>
from.start <= ctx.now && from.finish > ctx.now,
from: ({ from }) => from,
@@ -13,9 +13,9 @@ export type UserStatusInput = user.UserStatus & {
userID: string;
};
export const UserStatus: Required<
GQLUserStatusTypeResolver<UserStatusInput>
> = {
export const UserStatus: Required<GQLUserStatusTypeResolver<
UserStatusInput
>> = {
current: (status, input, ctx) => {
const consolidatedStatus = user.consolidateUserStatus(status, ctx.now);
const statuses: GQLUSER_STATUS[] = [];
@@ -1,9 +1,9 @@
import { GQLUsernameHistoryTypeResolver } from "coral-server/graph/schema/__generated__/types";
import * as user from "coral-server/models/user";
export const UsernameHistory: Required<
GQLUsernameHistoryTypeResolver<user.UsernameHistory>
> = {
export const UsernameHistory: Required<GQLUsernameHistoryTypeResolver<
user.UsernameHistory
>> = {
createdBy: ({ createdBy }, input, ctx) => {
if (createdBy) {
return ctx.loaders.Users.user.load(createdBy);
@@ -5,9 +5,9 @@ export type UsernameStatusInput = user.ConsolidatedUsernameStatus & {
userID: string;
};
export const UsernameStatus: Required<
GQLUsernameStatusTypeResolver<UsernameStatusInput>
> = {
export const UsernameStatus: Required<GQLUsernameStatusTypeResolver<
UsernameStatusInput
>> = {
history: ({ history, userID }) =>
history.map(status => ({ ...status, userID })),
history.map((status) => ({ ...status, userID })),
};
@@ -2,9 +2,7 @@ import * as tenant from "coral-server/models/tenant";
import { GQLWebhookEndpointTypeResolver } from "coral-server/graph/schema/__generated__/types";
export const WebhookEndpoint: GQLWebhookEndpointTypeResolver<
tenant.Endpoint
> = {
export const WebhookEndpoint: GQLWebhookEndpointTypeResolver<tenant.Endpoint> = {
signingSecret: ({ signingSecrets }) =>
signingSecrets[signingSecrets.length - 1],
};
+77 -44
View File
@@ -6,86 +6,119 @@ import Cursor from "./cursor";
describe("parseLiteral", () => {
it("parses a date from a string", () => {
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "2018-07-16T18:34:26.744Z",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "2018-07-16T18:34:26.744Z",
},
null
)
).toBeInstanceOf(Date);
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "this-should-fail",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "this-should-fail",
},
null
)
).toEqual(null);
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "",
},
null
)
).toEqual(null);
});
it("parses a number from a string", () => {
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "20",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "20",
},
null
)
).toEqual(20);
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "0",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "0",
},
null
)
).toEqual(0);
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "null",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "null",
},
null
)
).toEqual(null);
expect(
Cursor.parseLiteral({
kind: Kind.STRING,
value: "0",
})
Cursor.parseLiteral(
{
kind: Kind.STRING,
value: "0",
},
null
)
).toEqual(0);
});
it("parses a number from a number", () => {
expect(
Cursor.parseLiteral({
kind: Kind.INT,
value: "20",
})
Cursor.parseLiteral(
{
kind: Kind.INT,
value: "20",
},
null
)
).toEqual(20);
expect(
Cursor.parseLiteral({
kind: Kind.INT,
value: "0",
})
Cursor.parseLiteral(
{
kind: Kind.INT,
value: "0",
},
null
)
).toEqual(0);
expect(
Cursor.parseLiteral({
kind: Kind.INT,
value: "",
})
Cursor.parseLiteral(
{
kind: Kind.INT,
value: "",
},
null
)
).toEqual(null);
});
it("does not parse unknown kinds", () => {
expect(
Cursor.parseLiteral({
kind: Kind.FLOAT,
value: "0.0",
})
Cursor.parseLiteral(
{
kind: Kind.FLOAT,
value: "0.0",
},
null
)
).toEqual(null);
});
});
+21 -12
View File
@@ -5,27 +5,36 @@ import Locale from "./locale";
describe("parseLiteral", () => {
it("parses a valid locale from a string", () => {
expect(
Locale.parseLiteral({
kind: Kind.STRING,
value: "en-US",
})
Locale.parseLiteral(
{
kind: Kind.STRING,
value: "en-US",
},
null
)
).toBe("en-US");
});
it("parses an unsupported locale from a string", () => {
expect(() =>
Locale.parseLiteral({
kind: Kind.STRING,
value: "xyz",
})
Locale.parseLiteral(
{
kind: Kind.STRING,
value: "xyz",
},
null
)
).toThrow();
});
it("throws when not a string", () => {
expect(() =>
Locale.parseLiteral({
kind: Kind.INT,
value: "4",
})
Locale.parseLiteral(
{
kind: Kind.INT,
value: "4",
},
null
)
).toThrow();
});
});
@@ -15,7 +15,7 @@ import {
} from "subscriptions-transport-ws";
import { ACCESS_TOKEN_PARAM, CLIENT_ID_PARAM } from "coral-common/constants";
import { Omit, RequireProperty } from "coral-common/types";
import { RequireProperty } from "coral-common/types";
import { AppOptions } from "coral-server/app";
import { getHostname } from "coral-server/app/helpers/hostname";
import {
@@ -195,7 +195,7 @@ export function formatResponse(
if (value.errors && value.errors.length > 0) {
return {
...value,
errors: value.errors.map(err => {
errors: value.errors.map((err) => {
const enriched = enrichError(context, err);
// Log the error out.