From 77ad5ce70ad105ae2e7061ef885323945a995028 Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Sun, 20 Feb 2022 00:51:17 -0800 Subject: [PATCH] Expand GraphQL schema --- .../api/graphql/SkinsConnection.ts | 61 +++++++++++++ .../api/graphql/TweetsConnection.ts | 40 +++++++++ packages/skin-database/api/graphql/index.ts | 90 +++++++------------ .../api/graphql/resolvers/UserResolver.ts | 2 +- 4 files changed, 134 insertions(+), 59 deletions(-) create mode 100644 packages/skin-database/api/graphql/SkinsConnection.ts create mode 100644 packages/skin-database/api/graphql/TweetsConnection.ts diff --git a/packages/skin-database/api/graphql/SkinsConnection.ts b/packages/skin-database/api/graphql/SkinsConnection.ts new file mode 100644 index 00000000..4daf8990 --- /dev/null +++ b/packages/skin-database/api/graphql/SkinsConnection.ts @@ -0,0 +1,61 @@ +import SkinModel from "../../data/SkinModel"; +import * as Skins from "../../data/skins"; +import { knex } from "../../db"; +import SkinResolver from "./resolvers/SkinResolver"; + +export default class SkinsConnection { + _first: number; + _offset: number; + _sort: string; + _filter: string; + constructor(first: number, offset: number, sort: string, filter: string) { + this._first = first; + this._offset = offset; + this._filter = filter; + this._sort = sort; + } + _getQuery() { + let query = knex("skins").where({ skin_type: 1 }); + + if (this._filter === "APPROVED") { + query = query + .leftJoin("skin_reviews", "skin_reviews.skin_md5", "=", "skins.md5") + .where("review", "APPROVED"); + } + + return query; + } + + async count() { + const count = await this._getQuery().count("*", { as: "count" }); + return count[0].count; + } + + async nodes(args, ctx) { + if (this._sort === "MUSEUM") { + if (this._filter) { + throw new Error( + "We don't support combining sorting and filtering at the same time." + ); + } + const items = await Skins.getMuseumPage({ + first: this._first, + offset: this._offset, + }); + return Promise.all( + items.map(async (item) => { + const model = await SkinModel.fromMd5Assert(ctx, item.md5); + return new SkinResolver(model); + }) + ); + } + + const skins = await this._getQuery() + .select() + .limit(this._first) + .offset(this._offset); + return skins.map((skin) => { + return new SkinResolver(new SkinModel(ctx, skin)); + }); + } +} diff --git a/packages/skin-database/api/graphql/TweetsConnection.ts b/packages/skin-database/api/graphql/TweetsConnection.ts new file mode 100644 index 00000000..fff742e6 --- /dev/null +++ b/packages/skin-database/api/graphql/TweetsConnection.ts @@ -0,0 +1,40 @@ +import TweetModel from "../../data/TweetModel"; +import { knex } from "../../db"; +import TweetResolver from "./resolvers/TweetResolver"; + +export default class TweetsConnection { + _first: number; + _offset: number; + _sort: string; + constructor(first: number, offset: number, sort: string) { + this._first = first; + this._offset = offset; + this._sort = sort; + } + _getQuery() { + let query = knex("tweets"); + + if (this._sort === "LIKES") { + query = query.orderBy("likes", "desc"); + } else if (this._sort === "RETWEETS") { + query = query.orderBy("retweets", "desc"); + } + + return query; + } + + async count() { + const count = await this._getQuery().count("*", { as: "count" }); + return count[0].count; + } + + async nodes(args, ctx) { + const tweets = await this._getQuery() + .select() + .limit(this._first) + .offset(this._offset); + return tweets.map((tweet) => { + return new TweetResolver(new TweetModel(ctx, tweet)); + }); + } +} diff --git a/packages/skin-database/api/graphql/index.ts b/packages/skin-database/api/graphql/index.ts index 580de35c..bfc80eca 100644 --- a/packages/skin-database/api/graphql/index.ts +++ b/packages/skin-database/api/graphql/index.ts @@ -3,10 +3,10 @@ import { graphqlHTTP } from "express-graphql"; import { buildSchema } from "graphql"; import SkinModel from "../../data/SkinModel"; -import { knex } from "../../db"; import SkinResolver from "./resolvers/SkinResolver"; -import * as Skins from "../../data/skins"; import UserResolver from "./resolvers/UserResolver"; +import SkinsConnection from "./SkinsConnection"; +import TweetsConnection from "./TweetsConnection"; const router = Router(); @@ -123,9 +123,23 @@ type InternetArchiveItem { skin: Skin } +"""A collection of tweets made by the @winampskins bot""" +type TweetsConnection { + """The total number of tweets""" + count: Int + + """The list of tweets""" + nodes: [Tweet] +} + +enum TweetsSortOption { + LIKES + RETWEETS +} + """A collection of classic Winamp skins""" type SkinsConnection { - """The total number of skins""" + """The total number of skins matching the filter""" count: Int """The list of skins""" @@ -175,63 +189,17 @@ type Query { sort: SkinsSortOption, filter: SkinsFilterOption ): SkinsConnection + + """ + Tweets tweeted by @winampskins + """ + tweets( + first: Int, + offset: Int, + sort: TweetsSortOption + ): TweetsConnection }`); -class SkinsConnection { - _first: number; - _offset: number; - _sort: string; - _filter: string; - constructor(first: number, offset: number, sort: string, filter: string) { - this._first = first; - this._offset = offset; - this._filter = filter; - this._sort = sort; - } - async count() { - const count = await knex("skins") - .where({ skin_type: 1 }) - .count("*", { as: "count" }); - return count[0].count; - } - async nodes(args, ctx) { - if (this._sort === "MUSEUM") { - if (this._filter) { - throw new Error( - "We don't support combining sorting and filtering at the same time." - ); - } - const items = await Skins.getMuseumPage({ - first: this._first, - offset: this._offset, - }); - return Promise.all( - items.map(async (item) => { - const model = await SkinModel.fromMd5Assert(ctx, item.md5); - return new SkinResolver(model); - }) - ); - } - - let query = knex("skins"); - - if (this._filter === "APPROVED") { - query = query - .leftJoin("skin_reviews", "skin_reviews.skin_md5", "=", "skins.md5") - .where("review", "APPROVED"); - } - - const skins = await query - .where({ skin_type: 1 }) - .select() - .limit(this._first) - .offset(this._offset); - return skins.map((skin) => { - return new SkinResolver(new SkinModel(ctx, skin)); - }); - } -} - const root = { async fetch_skin_by_md5({ md5 }, { ctx }) { const skin = await SkinModel.fromMd5(ctx, md5); @@ -246,6 +214,12 @@ const root = { } return new SkinsConnection(first, offset, sort, filter); }, + async tweets({ first, offset, sort }) { + if (first > 1000) { + throw new Error("Maximum limit is 1000"); + } + return new TweetsConnection(first, offset, sort); + }, me() { return new UserResolver(); }, diff --git a/packages/skin-database/api/graphql/resolvers/UserResolver.ts b/packages/skin-database/api/graphql/resolvers/UserResolver.ts index 7cd46c15..2bbac658 100644 --- a/packages/skin-database/api/graphql/resolvers/UserResolver.ts +++ b/packages/skin-database/api/graphql/resolvers/UserResolver.ts @@ -1,6 +1,6 @@ export default class UserResolver { username(_args, ctx) { // For now every user is the current user. - return ctx.user.username; + return ctx.user?.username; } }