@zennomi/mangadex-full-api
Version:
A MangaDex api based around the official API.
132 lines (117 loc) • 5.11 kB
text/typescript
import { RelationshipSchema } from '../types/schema';
import IDObject from '../internal/IDObject';
// eslint-disable-next-line @typescript-eslint/ban-types
type GettableClass<T> = Function & { get: (id: string) => Promise<T>; getMultiple?: (ids: string[]) => Promise<T[]> };
type PartialRelationshipSchema = Pick<RelationshipSchema, 'id' | 'type'> &
Partial<Pick<RelationshipSchema, 'related' | 'attributes'>>;
/**
* Represents a relationship from one MangaDex object to another such as a manga, author, etc via its id.
*/
class Relationship<T> extends IDObject {
/**
* The MangaDex UUID of the object this relationship refers to
*/
id: string;
/**
* What type of object is this a relationship to
*/
type: string;
/**
* If the relationship is between two manga, how are they related?
*/
related?: RelationshipSchema['related'];
/**
* Cached relationships are created by using the 'includes' parameter on search requests or
* other functions that support it. For every type included in the parameter, relationships of
* that type will be replaced by the actual object. The object will still be represented
* by a Relationship object, but the {@link resolve} method will return instantly with the cached data.
* Each resulting object will have no further relationships of its own.
*/
cached = false;
private cachedData?: object;
private static typeMap: Record<string, GettableClass<unknown>> = {};
private static typeMapLocked = false;
constructor(data: PartialRelationshipSchema) {
super();
this.id = data.id;
if (!(data.type in Relationship.typeMap)) throw `Unregistered relationship type: ${data.type}`;
this.type = data.type;
this.related = data.related;
// Attempt to create cached object for reference expanded relationships
if (data.attributes) {
try {
const classObj = Relationship.typeMap[this.type];
// Attempt to simulate a common schema object:
const schemaObj = {
attributes: data.attributes,
id: this.id,
type: this.type,
relationships: [],
};
this.cachedData = Reflect.construct(classObj, [schemaObj]);
this.cached = true;
} catch (err) {
// console.log('Failed to create cache object');
// console.error(err);
}
}
}
/**
* This will automatically fetch the associated object that this relationship refers to.
* In other words, it wil call Manga.get(id), Chapter.get(id), etc with the information
* stored in this relationship instance. If this relationship is cached, then the resulting
* object will not have any relationships of its own.
*/
async resolve(): Promise<T> {
if (this.cached) return this.cachedData as T;
return Relationship.typeMap[this.type].get(this.id) as Promise<T>;
}
/**
* This will {@link Relationship.resolve} an array of relationships, returning another array
* in the same order.
* @param relationshipArray - An array of relationships of the same type
*/
static async resolveAll<T>(relationshipArray: Relationship<T>[]): Promise<T[]> {
if (relationshipArray.length === 0) return [];
const classObj = Relationship.typeMap[relationshipArray[0].type] as GettableClass<T>;
if (classObj !== undefined && classObj.getMultiple !== undefined) {
return classObj.getMultiple(relationshipArray.map((i) => i.id));
} else {
return await Promise.all(relationshipArray.map((elem) => elem.resolve()));
}
}
/**
* This will search through a relationship response from MangaDex and convert any
* relationships of a specific type into relationship objects.
* @internal
*/
static convertType<T2>(type: string, arr: RelationshipSchema[]): Relationship<T2>[] {
return arr.filter((elem) => elem.type === type).map((elem) => new Relationship<T2>(elem));
}
/**
* This function is used to resolved circular references, and should only be used in base.ts.
* Specifically, it pairs a relationship type to its associated class.
* @internal
*/
static registerTypes(types: string[], classObj: GettableClass<unknown>) {
if (Relationship.typeMapLocked) {
throw Error(`Cannot add types ${types} because the Relationship type map has been locked.`);
}
types.forEach((type) => (Relationship.typeMap[type] = classObj));
}
/**
* Lock the type map so that no more types can be registered.
* @internal
*/
static lockTypeMap() {
Relationship.typeMapLocked = true;
}
/**
* Returns an array of all registered Relationship types
* @internal
*/
static getRegisteredTypes() {
return Object.keys(Relationship.typeMap);
}
}
export default Relationship;