Loggest thine Stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

185 lines
8.0 KiB

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);
}