From ace904f3570ab8e62097e2c19c55d77cd3c547dc Mon Sep 17 00:00:00 2001 From: Jordan Eldredge Date: Tue, 29 Aug 2023 13:53:36 -0700 Subject: [PATCH] Migrate archiveFile graphql files to model --- .../graphql/resolvers/ArchiveFileResolver.ts | 81 ------------------- .../graphql/resolvers/ClassicSkinResolver.ts | 4 +- .../graphql/resolvers/CommonSkinResolver.ts | 9 +-- .../graphql/resolvers/ModernSkinResolver.ts | 4 +- .../api/graphql/resolvers/RootResolver.ts | 9 +-- .../skin-database/api/graphql/schema.graphql | 14 ++-- .../skin-database/data/ArchiveFileModel.ts | 56 ++++++++++++- 7 files changed, 71 insertions(+), 106 deletions(-) delete mode 100644 packages/skin-database/api/graphql/resolvers/ArchiveFileResolver.ts diff --git a/packages/skin-database/api/graphql/resolvers/ArchiveFileResolver.ts b/packages/skin-database/api/graphql/resolvers/ArchiveFileResolver.ts deleted file mode 100644 index 41d5e812..00000000 --- a/packages/skin-database/api/graphql/resolvers/ArchiveFileResolver.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Int } from "grats"; -import ArchiveFileModel from "../../../data/ArchiveFileModel"; -import SkinModel from "../../../data/SkinModel"; -import { ISkin } from "./CommonSkinResolver"; -import SkinResolver from "./SkinResolver"; - -/** - * A file found within a Winamp Skin's .wsz archive - * @gqlType ArchiveFile - */ -export default class ArchiveFileResolver { - _model: ArchiveFileModel; - constructor(model: ArchiveFileModel) { - this._model = model; - } - - /** - * Filename of the file within the archive - * @gqlField - */ - filename(): string { - return this._model.getFileName(); - } - /** - * A URL to download the file. **Note:** This is powered by a little - * serverless Cloudflare function which tries to exctact the file on the fly. - * It may not work for all files. - * @gqlField - */ - url(): string { - return this._model.getUrl(); - } - /** - * The md5 hash of the file within the archive - * @gqlField - */ - file_md5(): string { - return this._model.getFileMd5(); - } - /** - * The uncompressed size of the file in bytes. - * - * **Note:** Will be `null` for directories - * @gqlField - */ - size(): Promise { - return this._model.getFileSize(); - } - /** - * The content of the file, if it's a text file - * @gqlField - */ - text_content(): Promise { - return this._model.getTextContent(); - } - /** - * Is the file a directory? - * @gqlField - */ - is_directory(): boolean { - return this._model.getIsDirectory(); - } - - /** - * The skin in which this file was found - * @gqlField - */ - async skin(_: never, { ctx }): Promise { - const model = await SkinModel.fromMd5Assert(ctx, this._model.getMd5()); - return SkinResolver.fromModel(model); - } - - /** - * The date on the file inside the archive. Given in simplified extended ISO - * format (ISO 8601). - * @gqlField - */ - date(): string { - return this._model.getFileDate().toISOString(); - } -} diff --git a/packages/skin-database/api/graphql/resolvers/ClassicSkinResolver.ts b/packages/skin-database/api/graphql/resolvers/ClassicSkinResolver.ts index 01e7c18d..63f577e7 100644 --- a/packages/skin-database/api/graphql/resolvers/ClassicSkinResolver.ts +++ b/packages/skin-database/api/graphql/resolvers/ClassicSkinResolver.ts @@ -3,9 +3,9 @@ import { NodeResolver, toId } from "./NodeResolver"; import ReviewResolver from "./ReviewResolver"; import path from "path"; import { ID, Int } from "grats"; -import ArchiveFileResolver from "./ArchiveFileResolver"; import InternetArchiveItemResolver from "./InternetArchiveItemResolver"; import TweetModel from "../../../data/TweetModel"; +import ArchiveFileModel from "../../../data/ArchiveFileModel"; /** * A classic Winamp skin @@ -70,7 +70,7 @@ export default class ClassicSkinResolver /** * List of files contained within the skin's .wsz archive * @gqlField */ - archive_files(): Promise> { + archive_files(): Promise> { return super.archive_files(); } diff --git a/packages/skin-database/api/graphql/resolvers/CommonSkinResolver.ts b/packages/skin-database/api/graphql/resolvers/CommonSkinResolver.ts index 84d6ec25..d9a26dc2 100644 --- a/packages/skin-database/api/graphql/resolvers/CommonSkinResolver.ts +++ b/packages/skin-database/api/graphql/resolvers/CommonSkinResolver.ts @@ -1,9 +1,9 @@ import { ID } from "grats"; import SkinModel from "../../../data/SkinModel"; -import ArchiveFileResolver from "./ArchiveFileResolver"; import InternetArchiveItemResolver from "./InternetArchiveItemResolver"; import ReviewResolver from "./ReviewResolver"; import TweetModel from "../../../data/TweetModel"; +import ArchiveFileModel from "../../../data/ArchiveFileModel"; /** * A Winamp skin. Could be modern or classic. @@ -63,7 +63,7 @@ export interface ISkin { * List of files contained within the skin's .wsz archive * @gqlField */ - archive_files(): Promise>; + archive_files(): Promise>; /** * The skin's "item" at archive.org @@ -127,9 +127,8 @@ export default abstract class CommonSkinResolver { async tweets(): Promise> { return this._model.getTweets(); } - async archive_files(): Promise> { - const files = await this._model.getArchiveFiles(); - return files.map((file) => new ArchiveFileResolver(file)); + async archive_files(): Promise> { + return this._model.getArchiveFiles(); } async internet_archive_item(): Promise { const item = await this._model.getIaItem(); diff --git a/packages/skin-database/api/graphql/resolvers/ModernSkinResolver.ts b/packages/skin-database/api/graphql/resolvers/ModernSkinResolver.ts index 205f5c24..7b488297 100644 --- a/packages/skin-database/api/graphql/resolvers/ModernSkinResolver.ts +++ b/packages/skin-database/api/graphql/resolvers/ModernSkinResolver.ts @@ -5,9 +5,9 @@ import path from "path"; import { ID } from "grats"; import ReviewResolver from "./ReviewResolver"; import InternetArchiveItemResolver from "./InternetArchiveItemResolver"; -import ArchiveFileResolver from "./ArchiveFileResolver"; import { XMLParser } from "fast-xml-parser"; import TweetModel from "../../../data/TweetModel"; +import ArchiveFileModel from "../../../data/ArchiveFileModel"; /** * A "modern" Winamp skin. These skins use the `.wal` file extension and are free-form. @@ -78,7 +78,7 @@ export default class ModernSkinResolver /** * List of files contained within the skin's .wsz archive * @gqlField */ - async archive_files(): Promise> { + async archive_files(): Promise> { return super.archive_files(); } diff --git a/packages/skin-database/api/graphql/resolvers/RootResolver.ts b/packages/skin-database/api/graphql/resolvers/RootResolver.ts index d596925c..66774a4e 100644 --- a/packages/skin-database/api/graphql/resolvers/RootResolver.ts +++ b/packages/skin-database/api/graphql/resolvers/RootResolver.ts @@ -13,7 +13,6 @@ import algoliasearch from "algoliasearch"; import MutationResolver from "./MutationResolver"; import { knex } from "../../../db"; import ArchiveFileModel from "../../../data/ArchiveFileModel"; -import ArchiveFileResolver from "./ArchiveFileResolver"; import DatabaseStatisticsResolver from "./DatabaseStatisticsResolver"; import { fromId, NodeResolver } from "./NodeResolver"; import ModernSkinsConnection from "../ModernSkinsConnection"; @@ -104,12 +103,8 @@ class RootResolver extends MutationResolver { async fetch_archive_file_by_md5( { md5 }: { md5: string }, { ctx } - ): Promise { - const archiveFile = await ArchiveFileModel.fromFileMd5(ctx, md5); - if (archiveFile == null) { - return null; - } - return new ArchiveFileResolver(archiveFile); + ): Promise { + return ArchiveFileModel.fromFileMd5(ctx, md5); } /** diff --git a/packages/skin-database/api/graphql/schema.graphql b/packages/skin-database/api/graphql/schema.graphql index bfddd5e8..a1168baf 100644 --- a/packages/skin-database/api/graphql/schema.graphql +++ b/packages/skin-database/api/graphql/schema.graphql @@ -13,29 +13,29 @@ type ArchiveFile { The date on the file inside the archive. Given in simplified extended ISO format (ISO 8601). """ - date: String + date: String @methodName(name: "getIsoDate") """The md5 hash of the file within the archive""" - file_md5: String + file_md5: String @methodName(name: "getFileMd5") """Filename of the file within the archive""" - filename: String + filename: String @methodName(name: "getFileName") """Is the file a directory?""" - is_directory: Boolean + is_directory: Boolean @methodName(name: "getIsDirectory") """ The uncompressed size of the file in bytes. **Note:** Will be `null` for directories """ - size: Int + size: Int @methodName(name: "getFileSize") """The skin in which this file was found""" skin: Skin """The content of the file, if it's a text file""" - text_content: String + text_content: String @methodName(name: "getTextContent") """ A URL to download the file. **Note:** This is powered by a little serverless Cloudflare function which tries to exctact the file on the fly. It may not work for all files. """ - url: String + url: String @methodName(name: "getUrl") } """A classic Winamp skin""" diff --git a/packages/skin-database/data/ArchiveFileModel.ts b/packages/skin-database/data/ArchiveFileModel.ts index c891d9a5..3a7bdb7f 100644 --- a/packages/skin-database/data/ArchiveFileModel.ts +++ b/packages/skin-database/data/ArchiveFileModel.ts @@ -4,11 +4,18 @@ import DataLoader from "dataloader"; import { knex } from "../db"; import SkinModel from "./SkinModel"; import FileInfoModel from "./FileInfoModel"; +import { ISkin } from "../api/graphql/resolvers/CommonSkinResolver"; +import SkinResolver from "../api/graphql/resolvers/SkinResolver"; +import { Int } from "grats"; export type ArchiveFileDebugData = { row: ArchiveFileRow; }; +/** + * A file found within a Winamp Skin's .wsz archive + * @gqlType ArchiveFile + */ export default class ArchiveFileModel { constructor(readonly ctx: UserContext, readonly row: ArchiveFileRow) {} @@ -38,10 +45,18 @@ export default class ArchiveFileModel { return this.row.skin_md5; } + /** + * The md5 hash of the file within the archive + * @gqlField file_md5 + */ getFileMd5(): string { return this.row.file_md5; } + /** + * Filename of the file within the archive + * @gqlField filename + */ getFileName(): string { return this.row.file_name; } @@ -50,8 +65,22 @@ export default class ArchiveFileModel { return new Date(this.row.file_date); } - // Null if directory - async getFileSize(): Promise { + /** + * The date on the file inside the archive. Given in simplified extended ISO + * format (ISO 8601). + * @gqlField date + */ + getIsoDate(): string { + return this.getFileDate().toISOString(); + } + + /** + * The uncompressed size of the file in bytes. + * + * **Note:** Will be `null` for directories + * @gqlField size + */ + async getFileSize(): Promise { const info = await this._getFileInfo(); if (info == null) { return null; @@ -59,6 +88,10 @@ export default class ArchiveFileModel { return info.getFileSize(); } + /** + * The content of the file, if it's a text file + * @gqlField text_content + */ async getTextContent(): Promise { const info = await this._getFileInfo(); if (info == null) { @@ -67,10 +100,20 @@ export default class ArchiveFileModel { return info.getTextContent(); } + /** + * Is the file a directory? + * @gqlField is_directory + */ getIsDirectory(): boolean { return Boolean(this.row.is_directory); } + /** + * A URL to download the file. **Note:** This is powered by a little + * serverless Cloudflare function which tries to exctact the file on the fly. + * It may not work for all files. + * @gqlField url + */ getUrl(): string { const filename = encodeURIComponent(this.getFileName()); return `https://zip-worker.jordan1320.workers.dev/zip/${this.getMd5()}/${filename}`; @@ -80,6 +123,15 @@ export default class ArchiveFileModel { return SkinModel.fromMd5Assert(this.ctx, this.getMd5()); } + /** + * The skin in which this file was found + * @gqlField skin + */ + async skin(): Promise { + const model = await SkinModel.fromMd5Assert(this.ctx, this.getMd5()); + return SkinResolver.fromModel(model); + } + // Let's try to keep this as an implementation detail async _getFileInfo(): Promise { return FileInfoModel.fromFileMd5(this.ctx, this.getFileMd5());