Migrate archiveFile graphql files to model

This commit is contained in:
Jordan Eldredge 2023-08-29 13:53:36 -07:00
parent ba7989db9c
commit ace904f357
7 changed files with 71 additions and 106 deletions

View file

@ -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<Int | null> {
return this._model.getFileSize();
}
/**
* The content of the file, if it's a text file
* @gqlField
*/
text_content(): Promise<string | null> {
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<ISkin | null> {
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();
}
}

View file

@ -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<Array<ArchiveFileResolver | null>> {
archive_files(): Promise<Array<ArchiveFileModel | null>> {
return super.archive_files();
}

View file

@ -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<Array<ArchiveFileResolver | null>>;
archive_files(): Promise<Array<ArchiveFileModel | null>>;
/**
* The skin's "item" at archive.org
@ -127,9 +127,8 @@ export default abstract class CommonSkinResolver {
async tweets(): Promise<Array<TweetModel | null>> {
return this._model.getTweets();
}
async archive_files(): Promise<Array<ArchiveFileResolver | null>> {
const files = await this._model.getArchiveFiles();
return files.map((file) => new ArchiveFileResolver(file));
async archive_files(): Promise<Array<ArchiveFileModel | null>> {
return this._model.getArchiveFiles();
}
async internet_archive_item(): Promise<InternetArchiveItemResolver | null> {
const item = await this._model.getIaItem();

View file

@ -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<Array<ArchiveFileResolver | null>> {
async archive_files(): Promise<Array<ArchiveFileModel | null>> {
return super.archive_files();
}

View file

@ -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<ArchiveFileResolver | null> {
const archiveFile = await ArchiveFileModel.fromFileMd5(ctx, md5);
if (archiveFile == null) {
return null;
}
return new ArchiveFileResolver(archiveFile);
): Promise<ArchiveFileModel | null> {
return ArchiveFileModel.fromFileMd5(ctx, md5);
}
/**

View file

@ -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"""

View file

@ -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<number | null> {
/**
* 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<Int | null> {
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<string | null> {
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<ISkin | null> {
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<FileInfoModel | null> {
return FileInfoModel.fromFileMd5(this.ctx, this.getFileMd5());