|
|
import type { TimeInterval } from "$lib/models/common"; import type Item from "$lib/models/item"; import type { GroupItemsOptions, ItemFilter, ItemGroup, ItemInput } from "$lib/models/item"; import type Project from "$lib/models/project"; import type { ProjectEntry, ProjectInput, Requirement, RequirementInput } from "$lib/models/project"; import type { ScopeInput } from "$lib/models/scope"; import type Scope from "$lib/models/scope"; import type { SprintInput, SprintInputPart } from "$lib/models/sprint"; import type Sprint from "$lib/models/sprint"; import type Stat from "$lib/models/stat"; import type { StatInput } from "$lib/models/stat"; import type User from "$lib/models/user"; import type { AuthResult } from "$lib/models/user";
type FetchImpl = (info: RequestInfo, init?: RequestInit) => Promise<Response>;
export default class SL3APIClient { private fetcher: FetchImpl private root: string private tokenPromise: Promise<string>
constructor(fetcher: FetchImpl, tokenPromise?: Promise<string>, root?: string | boolean) { this.fetcher = fetcher;
if (typeof(root) === "string") { this.root = root || ""; } else if (root === true) { this.root = import.meta.env.VITE_STUFFLOG3_API; } else { this.root = "" }
this.tokenPromise = tokenPromise || null; }
async findScope(id: number): Promise<Scope> { return this.fetch<{scope: Scope}>("GET", `scopes/${id}`).then(r => r.scope); }
async listScopes(): Promise<Scope[]> { return this.fetch<{scopes: Scope[]}>("GET", "scopes").then(r => r.scopes); }
async createScope(scope: ScopeInput): Promise<Scope> { return this.fetch<{scope: Scope}>("POST", "scopes", scope).then(r => r.scope); }
async updateScope(id: number, scope: Partial<ScopeInput>): Promise<Scope> { return this.fetch<{scope: Scope}>("PUT", `scopes/${id}`, scope).then(r => r.scope); }
async listProjects(scopeId: number): Promise<ProjectEntry[]> { return this.fetch<{projects: ProjectEntry[]}>("GET", `scopes/${scopeId}/projects`).then(r => r.projects); }
async createRequirement(scopeId: number, projectId: number, input: RequirementInput): Promise<Requirement> { return this.fetch<{requirement: Requirement}>("POST", `scopes/${scopeId}/projects/${projectId}/requirements`, input).then(r => r.requirement); }
async updateRequirement(scopeId: number, projectId: number, requriementId: number, input: Partial<RequirementInput>): Promise<Requirement> { return this.fetch<{requirement: Requirement}>("PUT", `scopes/${scopeId}/projects/${projectId}/requirements/${requriementId}`, input).then(r => r.requirement); }
async findProject(scopeId: number, projectId: number): Promise<Project> { return this.fetch<{project: Project}>("GET", `scopes/${scopeId}/projects/${projectId}`).then(r => r.project); }
async createProject(scopeId: number, input: ProjectInput): Promise<Project> { return this.fetch<{project: Project}>("POST", `scopes/${scopeId}/projects`, input).then(r => r.project); }
async updateProject(scopeId: number, projectId: number, input: Partial<ProjectInput>): Promise<Project> { return this.fetch<{project: Project}>("PUT", `scopes/${scopeId}/projects/${projectId}`, input).then(r => r.project); }
async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped: true, groupOptions: GroupItemsOptions): Promise<{items: Item[], groups: ItemGroup[]}> async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped?: false): Promise<Item[]> async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped?: boolean, groupOptions?: GroupItemsOptions): Promise<Item[] | {items: Item[], groups: ItemGroup[]}> { const scopePrefix = (scopeId !== "ALL") ? `scopes/${scopeId}` : "all-scopes"; const groupedSuffix = !!grouped ? `&grouped=true&hideCreated=${groupOptions?.hideCreated}&hideAcquired=${groupOptions?.hideAcquired}&hideScheduled=${groupOptions?.hideScheduled}` : "";
return this.fetch<{items: Item[], groups: ItemGroup[]}>("GET", `${scopePrefix}/items?filter=${encodeURIComponent(JSON.stringify(filter))}${groupedSuffix}`).then(r => { if (!grouped) { return r.items }
return {groups: r.groups, items: r.items} }); }
async createItem(scopeId: number, input: ItemInput): Promise<Item> { return this.fetch<{item: Item}>("POST", `scopes/${scopeId}/items`, input).then(r => r.item); }
async updateItem(scopeId: number, itemId: number, input: Partial<ItemInput>): Promise<Item> { return this.fetch<{item: Item}>("PUT", `scopes/${scopeId}/items/${itemId}`, input).then(r => r.item); }
async createStat(scopeId: number, input: StatInput): Promise<Stat> { return this.fetch<{stat: Stat}>("POST", `scopes/${scopeId}/stats`, input).then(r => r.stat); }
async updateStat(scopeId: number, statId: number, input: Partial<StatInput>): Promise<Stat> { return this.fetch<{stat: Stat}>("PUT", `scopes/${scopeId}/stats/${statId}`, input).then(r => r.stat); }
async findSprint(scopeId: number, sprintId: number): Promise<Sprint> { return this.fetch<{sprint: Sprint}>("GET", `scopes/${scopeId}/sprints/${sprintId}`).then(r => r.sprint); }
async listSprints(scopeId: number | "ALL", interval?: TimeInterval<Date | string>): Promise<Sprint[]> { let qs = ""; if (interval != null) { const minStr = (interval.min instanceof Date) ? interval.min.toISOString() : interval.min; const maxStr = (interval.max instanceof Date) ? interval.max.toISOString() : interval.max; qs = `?from=${encodeURIComponent(minStr)}&to=${encodeURIComponent(maxStr)}` }
let prefix = (scopeId === "ALL") ? `all-scopes` : `scopes/${scopeId}` return this.fetch<{sprints: Sprint[]}>("GET", `${prefix}/sprints${qs}`).then(r => r.sprints); }
async createSprint(scopeId: number, input: SprintInput): Promise<Sprint> { return this.fetch<{sprint: Sprint}>("POST", `scopes/${scopeId}/sprints`, input).then(r => r.sprint); }
async updateSprint(scopeId: number, sprintId: number, input: Partial<SprintInput>): Promise<Sprint> { input = {...input, parts: void(0)}; return this.fetch<{sprint: Sprint}>("PUT", `scopes/${scopeId}/sprints/${sprintId}`, input).then(r => r.sprint); }
async upsertSprintPart(scopeId: number, sprintId: number, part: SprintInputPart): Promise<Sprint> { return this.fetch<{sprint: Sprint}>("PUT", `scopes/${scopeId}/sprints/${sprintId}/parts/${part.partId}`, part).then(r => r.sprint); }
async deleteSprintPart(scopeId: number, sprintId: number, part: SprintInputPart): Promise<Sprint> { return this.fetch<{sprint: Sprint}>("DELETE", `scopes/${scopeId}/sprints/${sprintId}/parts/${part.partId}`).then(r => r.sprint); }
async autchCheck(): Promise<User> { return this.fetch<{user: User}>("GET", "auth").then(r => r.user); }
async authLogin(username: string, password: string): Promise<AuthResult> { return this.fetch<{auth: AuthResult}>("POST", "auth/login", {username, password}).then(r => r.auth); }
async authRefresh(refreshToken: string): Promise<AuthResult> { return this.fetch<{auth: AuthResult}>("POST", "auth/refresh", {refreshToken}).then(r => r.auth); }
async fetch<T>(method: string, path: string, body: any = null): Promise<T> { const headers: Record<string, string> = {}; let bodyData: string | undefined = void(0); if (body != null) { headers["Content-Type"] = "application/json"; bodyData = JSON.stringify(body); }
if (this.tokenPromise != null) { headers["Authorization"] = `Bearer ${await this.tokenPromise}`; }
const res = await this.fetcher(`${this.root}/api/v1/${path}`, { method, headers, body: bodyData, credentials: "same-origin", }); if (!res.ok) { if (!(res.headers.get("content-type")||"").includes("application/json")) { throw await res.text(); }
throw await res.json(); }
return res.json(); } }
export function sl3(fetcher: FetchImpl, idToken?: string) { return new SL3APIClient(fetcher, idToken ? Promise.resolve(idToken) : null); }
|