Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 0 additions & 72 deletions api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ type Root {
me: User
user(username: String!): User
users(after: String, first: Int): UserConnection
story(slug: String!): Story
stories: [Story]
}

# An object with an ID
Expand Down Expand Up @@ -116,32 +114,6 @@ type UserEdge {
cursor: String!
}

type Story implements Node {
# The ID of an object
id: ID!
author: User!
slug: String!
title: String!
text(truncate: Int): String!
isURL: Boolean!
comments: [Comment]
pointsCount: Int!
pointGiven: Boolean!
commentsCount: Int!
createdAt(format: String): String
updatedAt(format: String): String
}

type Comment implements Node {
# The ID of an object
id: ID!
parent: Comment
author: User!
text: String
createdAt(format: String): String
updatedAt(format: String): String
}

type Mutation {
# Authenticates user with an ID token or email and password.
signIn(idToken: String, email: String, password: String): SignInPayload
Expand All @@ -151,15 +123,6 @@ type Mutation {

# Updates a user.
updateUser(input: UpdateUserInput!): UpdateUserPayload

# Deletes a user.
deleteUser(input: DeleteUserInput!): DeleteUserPayload

# Creates or updates a story.
upsertStory(input: UpsertStoryInput!): UpsertStoryPayload

# Marks the story as "liked".
likeStory(input: LikeStoryInput!): LikeStoryPayload
}

type SignInPayload {
Expand All @@ -183,38 +146,3 @@ input UpdateUserInput {
validateOnly: Boolean
clientMutationId: String
}

type DeleteUserPayload {
deletedUserId: String
clientMutationId: String
}

input DeleteUserInput {
id: ID!
clientMutationId: String
}

type UpsertStoryPayload {
story: Story
errors: [[String!]!]
clientMutationId: String
}

input UpsertStoryInput {
id: ID
title: String
text: String
approved: Boolean
validateOnly: Boolean
clientMutationId: String
}

type LikeStoryPayload {
story: Story
clientMutationId: String
}

input LikeStoryInput {
id: ID!
clientMutationId: String
}
115 changes: 6 additions & 109 deletions api/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import DataLoader from "dataloader";
import { Request } from "express";

import db, { User, Identity, Story, Comment } from "./db";
import { mapTo, mapToMany, mapToValues } from "./utils";
import db, { User, Identity } from "./db";
import { mapTo, mapToMany } from "./utils";
import { UnauthorizedError, ForbiddenError } from "./error";

export class Context {
Expand All @@ -24,6 +24,10 @@ export class Context {
}
}

/*
* Authentication and authorization
* ------------------------------------------------------------------------ */

get user(): User | null {
return this.req.user;
}
Expand All @@ -36,10 +40,6 @@ export class Context {
this.req.signOut();
}

/*
* Authorization
* ------------------------------------------------------------------------ */

ensureAuthorized(check?: (user: User) => boolean): void {
if (!this.req.user) {
throw new UnauthorizedError();
Expand Down Expand Up @@ -89,107 +89,4 @@ export class Context {
.select()
.then((rows) => mapToMany(rows, keys, (x) => x.user_id)),
);

storyById = new DataLoader<string, Story | null>((keys) =>
db
.table<Story>("stories")
.whereIn("id", keys)
.select()
.then((rows) => {
rows.forEach((x) => this.storyBySlug.prime(x.slug, x));
return rows;
})
.then((rows) => mapTo(rows, keys, (x) => x.id)),
);

storyBySlug = new DataLoader<string, Story | null>((keys) =>
db
.table<Story>("stories")
.whereIn("slug", keys)
.select()
.then((rows) => {
rows.forEach((x) => this.storyById.prime(x.id, x));
return rows;
})
.then((rows) => mapTo(rows, keys, (x) => x.slug)),
);

storyCommentsCount = new DataLoader<string, number>((keys) =>
db
.table<Comment>("comments")
.whereIn("story_id", keys)
.groupBy("story_id")
.select<{ story_id: string; count: string }[]>(
"story_id",
db.raw("count(story_id)"),
)
.then((rows) =>
mapToValues(
rows,
keys,
(x) => x.story_id,
(x) => (x ? Number(x.count) : 0),
),
),
);

storyPointsCount = new DataLoader<string, number>((keys) =>
db
.table("stories")
.leftJoin("story_points", "story_points.story_id", "stories.id")
.whereIn("stories.id", keys)
.groupBy("stories.id")
.select("stories.id", db.raw("count(story_points.user_id)::int"))
.then((rows) =>
mapToValues(
rows,
keys,
(x) => x.id,
(x) => (x ? parseInt(x.count, 10) : 0),
),
),
);

storyPointGiven = new DataLoader<string, boolean>((keys) => {
const currentUser = this.user;
const userId = currentUser ? currentUser.id : "";

return db
.table("stories")
.leftJoin("story_points", function join() {
this.on("story_points.story_id", "stories.id").andOn(
"story_points.user_id",
db.raw("?", [userId]),
);
})
.whereIn("stories.id", keys)
.select<{ id: string; given: boolean }[]>(
"stories.id",
db.raw("(story_points.user_id IS NOT NULL) AS given"),
)
.then((rows) =>
mapToValues(
rows,
keys,
(x) => x.id,
(x) => x?.given || false,
),
);
});

commentById = new DataLoader<string, Comment | null>((keys) =>
db
.table<Comment>("comments")
.whereIn("id", keys)
.select()
.then((rows) => mapTo(rows, keys, (x) => x.id)),
);

commentsByStoryId = new DataLoader<string, Comment[]>((keys) =>
db
.table<Comment>("comments")
.whereIn("story_id", keys)
.select()
.then((rows) => mapToMany(rows, keys, (x) => x.story_id)),
);
}
32 changes: 0 additions & 32 deletions api/src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,6 @@ export enum IdentityProvider {
playgames = "playgames",
}

export type CommentPoint = {
comment_id: string;
user_id: string;
};

export type Comment = {
id: string;
story_id: string;
parent_id: string | null;
author_id: string;
text: string | null;
created_at: Date;
updated_at: Date;
};

export type Identity = {
provider: IdentityProvider;
id: string;
Expand All @@ -86,23 +71,6 @@ export type Identity = {
expires_at: Date | null;
};

export type Story = {
id: string;
author_id: string;
slug: string;
title: string;
text: string | null;
is_url: boolean;
approved: boolean;
created_at: Date;
updated_at: Date;
};

export type StoryPoint = {
story_id: string;
user_id: string;
};

export type User = {
id: string;
username: string;
Expand Down
1 change: 0 additions & 1 deletion api/src/mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@

export * from "./auth";
export * from "./user";
export * from "./story";
Loading