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.

184 lines
8.0 KiB

2 years ago
2 years ago
2 years ago
2 years ago
  1. import type { TimeInterval } from "$lib/models/common";
  2. import type Item from "$lib/models/item";
  3. import type { GroupItemsOptions, ItemFilter, ItemGroup, ItemInput } from "$lib/models/item";
  4. import type Project from "$lib/models/project";
  5. import type { ProjectEntry, ProjectInput, Requirement, RequirementInput } from "$lib/models/project";
  6. import type { ScopeInput } from "$lib/models/scope";
  7. import type Scope from "$lib/models/scope";
  8. import type { SprintInput, SprintInputPart } from "$lib/models/sprint";
  9. import type Sprint from "$lib/models/sprint";
  10. import type Stat from "$lib/models/stat";
  11. import type { StatInput } from "$lib/models/stat";
  12. import type User from "$lib/models/user";
  13. import type { AuthResult } from "$lib/models/user";
  14. type FetchImpl = (info: RequestInfo, init?: RequestInit) => Promise<Response>;
  15. export default class SL3APIClient {
  16. private fetcher: FetchImpl
  17. private root: string
  18. private tokenPromise: Promise<string>
  19. constructor(fetcher: FetchImpl, tokenPromise?: Promise<string>, root?: string | boolean) {
  20. this.fetcher = fetcher;
  21. if (typeof(root) === "string") {
  22. this.root = root || "";
  23. } else if (root === true) {
  24. this.root = import.meta.env.VITE_STUFFLOG3_API;
  25. } else {
  26. this.root = ""
  27. }
  28. this.tokenPromise = tokenPromise || null;
  29. }
  30. async findScope(id: number): Promise<Scope> {
  31. return this.fetch<{scope: Scope}>("GET", `scopes/${id}`).then(r => r.scope);
  32. }
  33. async listScopes(): Promise<Scope[]> {
  34. return this.fetch<{scopes: Scope[]}>("GET", "scopes").then(r => r.scopes);
  35. }
  36. async createScope(scope: ScopeInput): Promise<Scope> {
  37. return this.fetch<{scope: Scope}>("POST", "scopes", scope).then(r => r.scope);
  38. }
  39. async updateScope(id: number, scope: Partial<ScopeInput>): Promise<Scope> {
  40. return this.fetch<{scope: Scope}>("PUT", `scopes/${id}`, scope).then(r => r.scope);
  41. }
  42. async listProjects(scopeId: number): Promise<ProjectEntry[]> {
  43. return this.fetch<{projects: ProjectEntry[]}>("GET", `scopes/${scopeId}/projects`).then(r => r.projects);
  44. }
  45. async createRequirement(scopeId: number, projectId: number, input: RequirementInput): Promise<Requirement> {
  46. return this.fetch<{requirement: Requirement}>("POST", `scopes/${scopeId}/projects/${projectId}/requirements`, input).then(r => r.requirement);
  47. }
  48. async updateRequirement(scopeId: number, projectId: number, requriementId: number, input: Partial<RequirementInput>): Promise<Requirement> {
  49. return this.fetch<{requirement: Requirement}>("PUT", `scopes/${scopeId}/projects/${projectId}/requirements/${requriementId}`, input).then(r => r.requirement);
  50. }
  51. async findProject(scopeId: number, projectId: number): Promise<Project> {
  52. return this.fetch<{project: Project}>("GET", `scopes/${scopeId}/projects/${projectId}`).then(r => r.project);
  53. }
  54. async createProject(scopeId: number, input: ProjectInput): Promise<Project> {
  55. return this.fetch<{project: Project}>("POST", `scopes/${scopeId}/projects`, input).then(r => r.project);
  56. }
  57. async updateProject(scopeId: number, projectId: number, input: Partial<ProjectInput>): Promise<Project> {
  58. return this.fetch<{project: Project}>("PUT", `scopes/${scopeId}/projects/${projectId}`, input).then(r => r.project);
  59. }
  60. async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped: true, groupOptions: GroupItemsOptions): Promise<{items: Item[], groups: ItemGroup[]}>
  61. async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped?: false): Promise<Item[]>
  62. async listItems(scopeId: number | "ALL", filter: ItemFilter, grouped?: boolean, groupOptions?: GroupItemsOptions): Promise<Item[] | {items: Item[], groups: ItemGroup[]}> {
  63. const scopePrefix = (scopeId !== "ALL") ? `scopes/${scopeId}` : "all-scopes";
  64. const groupedSuffix = !!grouped ? `&grouped=true&hideCreated=${groupOptions?.hideCreated}&hideAcquired=${groupOptions?.hideAcquired}&hideScheduled=${groupOptions?.hideScheduled}` : "";
  65. return this.fetch<{items: Item[], groups: ItemGroup[]}>("GET", `${scopePrefix}/items?filter=${encodeURIComponent(JSON.stringify(filter))}${groupedSuffix}`).then(r => {
  66. if (!grouped) {
  67. return r.items
  68. }
  69. return {groups: r.groups, items: r.items}
  70. });
  71. }
  72. async createItem(scopeId: number, input: ItemInput): Promise<Item> {
  73. return this.fetch<{item: Item}>("POST", `scopes/${scopeId}/items`, input).then(r => r.item);
  74. }
  75. async updateItem(scopeId: number, itemId: number, input: Partial<ItemInput>): Promise<Item> {
  76. return this.fetch<{item: Item}>("PUT", `scopes/${scopeId}/items/${itemId}`, input).then(r => r.item);
  77. }
  78. async createStat(scopeId: number, input: StatInput): Promise<Stat> {
  79. return this.fetch<{stat: Stat}>("POST", `scopes/${scopeId}/stats`, input).then(r => r.stat);
  80. }
  81. async updateStat(scopeId: number, statId: number, input: Partial<StatInput>): Promise<Stat> {
  82. return this.fetch<{stat: Stat}>("PUT", `scopes/${scopeId}/stats/${statId}`, input).then(r => r.stat);
  83. }
  84. async findSprint(scopeId: number, sprintId: number): Promise<Sprint> {
  85. return this.fetch<{sprint: Sprint}>("GET", `scopes/${scopeId}/sprints/${sprintId}`).then(r => r.sprint);
  86. }
  87. async listSprints(scopeId: number | "ALL", interval?: TimeInterval<Date | string>): Promise<Sprint[]> {
  88. let qs = "";
  89. if (interval != null) {
  90. const minStr = (interval.min instanceof Date) ? interval.min.toISOString() : interval.min;
  91. const maxStr = (interval.max instanceof Date) ? interval.max.toISOString() : interval.max;
  92. qs = `?from=${encodeURIComponent(minStr)}&to=${encodeURIComponent(maxStr)}`
  93. }
  94. let prefix = (scopeId === "ALL") ? `all-scopes` : `scopes/${scopeId}`
  95. return this.fetch<{sprints: Sprint[]}>("GET", `${prefix}/sprints${qs}`).then(r => r.sprints);
  96. }
  97. async createSprint(scopeId: number, input: SprintInput): Promise<Sprint> {
  98. return this.fetch<{sprint: Sprint}>("POST", `scopes/${scopeId}/sprints`, input).then(r => r.sprint);
  99. }
  100. async updateSprint(scopeId: number, sprintId: number, input: Partial<SprintInput>): Promise<Sprint> {
  101. input = {...input, parts: void(0)};
  102. return this.fetch<{sprint: Sprint}>("PUT", `scopes/${scopeId}/sprints/${sprintId}`, input).then(r => r.sprint);
  103. }
  104. async upsertSprintPart(scopeId: number, sprintId: number, part: SprintInputPart): Promise<Sprint> {
  105. return this.fetch<{sprint: Sprint}>("PUT", `scopes/${scopeId}/sprints/${sprintId}/parts/${part.partId}`, part).then(r => r.sprint);
  106. }
  107. async deleteSprintPart(scopeId: number, sprintId: number, part: SprintInputPart): Promise<Sprint> {
  108. return this.fetch<{sprint: Sprint}>("DELETE", `scopes/${scopeId}/sprints/${sprintId}/parts/${part.partId}`).then(r => r.sprint);
  109. }
  110. async autchCheck(): Promise<User> {
  111. return this.fetch<{user: User}>("GET", "auth").then(r => r.user);
  112. }
  113. async authLogin(username: string, password: string): Promise<AuthResult> {
  114. return this.fetch<{auth: AuthResult}>("POST", "auth/login", {username, password}).then(r => r.auth);
  115. }
  116. async authRefresh(refreshToken: string): Promise<AuthResult> {
  117. return this.fetch<{auth: AuthResult}>("POST", "auth/refresh", {refreshToken}).then(r => r.auth);
  118. }
  119. async fetch<T>(method: string, path: string, body: any = null): Promise<T> {
  120. const headers: Record<string, string> = {};
  121. let bodyData: string | undefined = void(0);
  122. if (body != null) {
  123. headers["Content-Type"] = "application/json";
  124. bodyData = JSON.stringify(body);
  125. }
  126. if (this.tokenPromise != null) {
  127. headers["Authorization"] = `Bearer ${await this.tokenPromise}`;
  128. }
  129. const res = await this.fetcher(`${this.root}/api/v1/${path}`, {
  130. method, headers,
  131. body: bodyData,
  132. credentials: "same-origin",
  133. });
  134. if (!res.ok) {
  135. if (!(res.headers.get("content-type")||"").includes("application/json")) {
  136. throw await res.text();
  137. }
  138. throw await res.json();
  139. }
  140. return res.json();
  141. }
  142. }
  143. export function sl3(fetcher: FetchImpl, idToken?: string) {
  144. return new SL3APIClient(fetcher, idToken ? Promise.resolve(idToken) : null);
  145. }