[CORL-963] Story sorting fix (#2887)

* fix: fixed sorting issues with queries

* review: naming touchups
This commit is contained in:
Wyatt Johnson
2020-03-19 18:59:34 +00:00
committed by GitHub
parent 18b274a6a9
commit 6db72db837
4 changed files with 100 additions and 20 deletions
+79 -4
View File
@@ -1,8 +1,10 @@
import DataLoader from "dataloader";
import { defaultTo } from "lodash";
import { DateTime } from "luxon";
import GraphContext from "coral-server/graph/context";
import { Connection } from "coral-server/models/helpers";
import { CloseCommenting } from "coral-server/models/settings";
import {
retrieveActiveStories,
retrieveManyStories,
@@ -26,16 +28,89 @@ import {
import { createManyBatchLoadFn } from "./util";
const statusFilter = (
status?: GQLSTORY_STATUS
closeCommenting: CloseCommenting,
status?: GQLSTORY_STATUS,
now = new Date()
): StoryConnectionInput["filter"] => {
switch (status) {
case GQLSTORY_STATUS.OPEN:
if (closeCommenting.auto) {
// Automatic story closing has been enabled. Stories will be considered
// open if they have a ${closedAt} date in the future, if they've been
// forced open (where ${closedAt} is set to false), or they haven't been
// closed and their ${createdAt} date is after ${now - close.timeout}.
// Calculate the cutoff time for createdAt.
const consideredClosedAt = DateTime.fromJSDate(now)
.plus({
seconds: -closeCommenting.timeout,
})
.toJSDate();
return {
$or: [
// The story will be open if the close date is in the future...
{
closedAt: { $gt: now },
},
// Or the story has been forced open by setting closedAt to false...
{
closedAt: false,
},
// Or the closed at date isn't set and the createdAt date is after
// the cutoff date.
{
closedAt: null,
createdAt: { $gt: consideredClosedAt },
},
],
};
}
// Automatic story closing is not enabled. Stories will be considered open
// if they do not have a ${closedAt}, it has been forced open (where
// ${closedAt} is set to false), or the ${closedAt} date is in the future.
return {
closedAt: { $in: [null, false] },
$or: [
// A story is open if the closedAt date is in the future...
{ closedAt: { $gt: now } },
// Or the closedAt date is not set.
{ closedAt: { $in: [null, false] } },
],
};
case GQLSTORY_STATUS.CLOSED:
if (closeCommenting.auto) {
// Automatic story closing has been enabled. Stories will be considered
// closed if they have a ${closedAt} date before the current date or
// they do not have a ${closedAt} date set and the ${createdAt} date is
// before ${now - close.timeout} (implying that it's close window is
// up).
// Calculate the cutoff time for createdAt.
const consideredClosedAt = DateTime.fromJSDate(now)
.plus({
seconds: -closeCommenting.timeout,
})
.toJSDate();
return {
$or: [
// The story will be closed if the closedAt date is in the past...
{ closedAt: { $lte: now } },
// Or the closedAt date isn't set and the createdAt date is before
// the cutoff date.
{
closedAt: null,
createdAt: { $lte: consideredClosedAt },
},
],
};
}
// Automatic story closing is not enabled. Stories will be considered
// closed if the ${closedAt} date is before ${now}.
return {
closedAt: { $lte: new Date() },
closedAt: { $lte: now },
};
default:
return {};
@@ -126,7 +201,7 @@ export default (ctx: GraphContext) => ({
// Merge the site filter into the connection filter.
...siteFilter(siteID),
// Merge the status filter into the connection filter.
...statusFilter(status),
...statusFilter(ctx.tenant.closeCommenting, status, ctx.now),
// Merge the query filters into the query.
...queryFilter(query),
},
+7 -10
View File
@@ -1,29 +1,26 @@
import { defaultsDeep } from "lodash";
import { decodeActionCounts } from "coral-server/models/action/comment";
import * as story from "coral-server/models/story";
import {
GQLSTORY_STATUS,
GQLStoryTypeResolver,
GQLTAG,
} from "coral-server/graph/schema/__generated__/types";
import { decodeActionCounts } from "coral-server/models/action/comment";
import * as story from "coral-server/models/story";
import GraphContext from "../context";
import { CommentCountsInput } from "./CommentCounts";
import { storyModerationInputResolver } from "./ModerationQueues";
const isStoryClosed = (s: story.Story, ctx: GraphContext) => {
const closedAt = story.getStoryClosedAt(ctx.tenant, s) || null;
return !!closedAt && new Date() >= closedAt;
};
export const Story: GQLStoryTypeResolver<story.Story> = {
comments: (s, input, ctx) => ctx.loaders.Comments.forStory(s.id, input),
featuredComments: (s, input, ctx) =>
ctx.loaders.Comments.taggedForStory(s.id, GQLTAG.FEATURED, input),
status: (s, input, ctx) =>
isStoryClosed(s, ctx) ? GQLSTORY_STATUS.CLOSED : GQLSTORY_STATUS.OPEN,
isClosed: (s, input, ctx) => isStoryClosed(s, ctx),
story.isStoryClosed(ctx.tenant, s, ctx.now)
? GQLSTORY_STATUS.CLOSED
: 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),
commentCounts: (s): CommentCountsInput => s,
+12 -3
View File
@@ -26,10 +26,19 @@ export function getStoryTitle(story: Pick<Story, "metadata" | "url">) {
: story.url;
}
export function isStoryClosed(
tenant: Pick<Tenant, "closeCommenting">,
story: Pick<Story, "closedAt" | "createdAt">,
now = new Date()
) {
const closedAt = getStoryClosedAt(tenant, story);
return closedAt && closedAt <= now;
}
export function getStoryClosedAt(
tenant: Pick<Tenant, "closeCommenting">,
story: Pick<Story, "closedAt" | "createdAt">
): Story["closedAt"] {
): Date | null {
// Try to get the closedAt time from the story.
if (story.closedAt) {
return story.closedAt;
@@ -37,7 +46,7 @@ export function getStoryClosedAt(
// Check to see if the story has been forced open again.
if (story.closedAt === false) {
return false;
return null;
}
// If the story hasn't already been closed, then check to see if the Tenant
@@ -50,5 +59,5 @@ export function getStoryClosedAt(
.toJSDate();
}
return;
return null;
}
@@ -1,5 +1,5 @@
import { StoryClosedError } from "coral-server/errors";
import { getStoryClosedAt } from "coral-server/models/story";
import { isStoryClosed } from "coral-server/models/story";
import {
IntermediatePhaseResult,
ModerationPhaseContext,
@@ -14,8 +14,7 @@ export const storyClosed = ({
ModerationPhaseContext,
"story" | "tenant" | "now"
>): IntermediatePhaseResult | void => {
const closedAt = getStoryClosedAt(tenant, story);
if (closedAt && closedAt <= now) {
if (isStoryClosed(tenant, story, now)) {
throw new StoryClosedError();
}
};