From 8e16ab39b59e1831e9102f96f5aebf5b707d54ea Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 1 May 2022 13:04:17 +0200 Subject: [PATCH] integrate api into frontend. --- api/items.go | 4 +- api/projects.go | 4 +- api/scope.go | 6 +- api/stats.go | 4 +- frontend/src/app.d.ts | 1 + frontend/src/hooks.ts | 7 +- .../lib/components/frontpage/ScopeLink.svelte | 2 +- frontend/src/lib/config.ts | 17 +- frontend/src/lib/database/interfaces.ts | 37 ---- frontend/src/lib/database/mysql/database.ts | 48 ----- frontend/src/lib/database/mysql/scopes.ts | 84 --------- frontend/src/lib/models/item.ts | 10 +- frontend/src/lib/models/project.ts | 7 +- frontend/src/lib/models/scope.ts | 14 +- frontend/src/lib/models/stat.ts | 32 ---- frontend/src/lib/utils/slugify.ts | 13 ++ frontend/src/routes/[scope].json.ts | 31 ---- frontend/src/routes/[scope]/__layout.svelte | 29 +-- frontend/src/routes/api/[...any]/index.ts | 30 +++ frontend/src/routes/indexdata.json.ts | 171 +----------------- internal/database/mysql/scopes.go | 4 + internal/models/item.go | 2 +- internal/models/scope.go | 8 +- 23 files changed, 103 insertions(+), 462 deletions(-) delete mode 100644 frontend/src/lib/database/interfaces.ts delete mode 100644 frontend/src/lib/database/mysql/database.ts delete mode 100644 frontend/src/lib/database/mysql/scopes.ts create mode 100644 frontend/src/lib/utils/slugify.ts delete mode 100644 frontend/src/routes/[scope].json.ts create mode 100644 frontend/src/routes/api/[...any]/index.ts diff --git a/api/items.go b/api/items.go index 92600bc..a997717 100644 --- a/api/items.go +++ b/api/items.go @@ -12,7 +12,7 @@ import ( func Items(g *gin.RouterGroup, db database.Database) { g.Use(scopeIDMiddleware(db)) - g.GET("/", handler("items", func(c *gin.Context) (res interface{}, err error) { + g.GET("", handler("items", func(c *gin.Context) (res interface{}, err error) { mode := c.Query("mode") var fromTime, toTime time.Time @@ -79,7 +79,7 @@ func Items(g *gin.RouterGroup, db database.Database) { return db.Items(getScope(c).ID).Find(c.Request.Context(), id) })) - g.POST("/", handler("item", func(c *gin.Context) (interface{}, error) { + g.POST("", handler("item", func(c *gin.Context) (interface{}, error) { item := &models.Item{} err := c.BindJSON(item) if err != nil { diff --git a/api/projects.go b/api/projects.go index b5ed46f..2f59bcc 100644 --- a/api/projects.go +++ b/api/projects.go @@ -12,7 +12,7 @@ import ( func Projects(g *gin.RouterGroup, db database.Database) { g.Use(scopeIDMiddleware(db)) - g.GET("/", handler("projects", func(c *gin.Context) (interface{}, error) { + g.GET("", handler("projects", func(c *gin.Context) (interface{}, error) { return db.Projects(getScope(c).ID).List(c.Request.Context()) })) @@ -25,7 +25,7 @@ func Projects(g *gin.RouterGroup, db database.Database) { return db.Projects(getScope(c).ID).Find(c.Request.Context(), id) })) - g.POST("/", handler("project", func(c *gin.Context) (interface{}, error) { + g.POST("", handler("project", func(c *gin.Context) (interface{}, error) { project := &models.Project{} err := c.BindJSON(project) if err != nil { diff --git a/api/scope.go b/api/scope.go index 5a314c4..9eb1ecc 100644 --- a/api/scope.go +++ b/api/scope.go @@ -9,7 +9,7 @@ import ( ) func Scopes(g *gin.RouterGroup, db database.Database) { - g.GET("/", handler("scopes", func(c *gin.Context) (interface{}, error) { + g.GET("", handler("scopes", func(c *gin.Context) (interface{}, error) { return db.Scopes().ListByUser(c.Request.Context(), auth.UserID(c)) })) @@ -23,9 +23,11 @@ func Scopes(g *gin.RouterGroup, db database.Database) { if err != nil { return nil, err } - if !scope.HasMember(auth.UserID(c)) { + member := scope.Member(auth.UserID(c)) + if member == nil { return nil, slerrors.NotFound("Scope") } + scope.DisplayName = member.Name scope.StatusLabels = models.StatusLabels return scope, nil diff --git a/api/stats.go b/api/stats.go index 8d38752..b736ddd 100644 --- a/api/stats.go +++ b/api/stats.go @@ -10,7 +10,7 @@ import ( func Stats(g *gin.RouterGroup, db database.Database) { g.Use(scopeIDMiddleware(db)) - g.GET("/", handler("stats", func(c *gin.Context) (interface{}, error) { + g.GET("", handler("stats", func(c *gin.Context) (interface{}, error) { return getScope(c).Stats, nil })) @@ -28,7 +28,7 @@ func Stats(g *gin.RouterGroup, db database.Database) { return stat, nil })) - g.POST("/", handler("stat", func(c *gin.Context) (interface{}, error) { + g.POST("", handler("stat", func(c *gin.Context) (interface{}, error) { stat := &models.Stat{} err := c.BindJSON(stat) if err != nil { diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index 20332a2..b0821e0 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -17,6 +17,7 @@ declare global { // interface Session {} interface Stuff { scope?: Scope + scopePath?: string } } } diff --git a/frontend/src/hooks.ts b/frontend/src/hooks.ts index 665658d..e5b5f58 100644 --- a/frontend/src/hooks.ts +++ b/frontend/src/hooks.ts @@ -1,11 +1,12 @@ import config from "$lib/config"; +import type { ScopeEntry } from "$lib/models/scope"; import type { Handle } from "@sveltejs/kit"; export const handle: Handle = async({ event, resolve }) => { if (process.env.STUFFLOG3_USE_DUMMY_USER === "true") { event.locals.user = { id: "c11230be-4912-4313-83b0-410a248b5bd1", - name: "Developer", + name: "DevMan", } } else { event.locals.user = {id: "", name: "Guest"}; @@ -14,8 +15,8 @@ export const handle: Handle = async({ event, resolve }) => { } if (event.locals.user.id !== "") { - const db = await config.database(); - event.locals.scopes = await db.withUser(event.locals.user.id).scopes().list() + const res: {scopes: ScopeEntry[]} = await fetch(`${process.env.STUFFLOG3_API}/api/scopes`).then(r => r.json()); + event.locals.scopes = res.scopes; } else { event.locals.scopes = []; } diff --git a/frontend/src/lib/components/frontpage/ScopeLink.svelte b/frontend/src/lib/components/frontpage/ScopeLink.svelte index a2e2830..9ff841e 100644 --- a/frontend/src/lib/components/frontpage/ScopeLink.svelte +++ b/frontend/src/lib/components/frontpage/ScopeLink.svelte @@ -6,7 +6,7 @@ export let scope: ScopeEntry - + {scope.name} diff --git a/frontend/src/lib/config.ts b/frontend/src/lib/config.ts index 2adb1c4..a3f995e 100644 --- a/frontend/src/lib/config.ts +++ b/frontend/src/lib/config.ts @@ -1,18 +1,3 @@ -import type { Database } from "./database/interfaces"; -import MysqlDB from "./database/mysql/database"; - -let databasePromise: Promise | null = null; -let databaseTime: number = 0; - -const config = { - database() { - if (databasePromise == null || Date.now() < (databaseTime - 60000)) { - databasePromise = MysqlDB.connectEnv(); - databaseTime = Date.now(); - } - - return databasePromise; - } -}; +const config = {}; export default config; \ No newline at end of file diff --git a/frontend/src/lib/database/interfaces.ts b/frontend/src/lib/database/interfaces.ts deleted file mode 100644 index f48c7ae..0000000 --- a/frontend/src/lib/database/interfaces.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; -import type Scope from "$lib/models/scope"; -import type { StatEntry } from "$lib/models/stat"; -import type Stat from "$lib/models/stat"; - -export class NotFoundError extends Error { - constructor(subject: string) { - super(`${subject} not found`); - } -} - -export interface Database { - userId: string - - scopes(): ScopeRepo - stats(scopeId: number): StatRepo - withUser(userId: string): Database -} - -export interface ScopeRepo { - userId: string - - find(id: number): Promise - list(): Promise - create(input: ScopeInput): Promise - update(id: number, input: Partial): Promise - delete(id: number): Promise -} - -export interface StatRepo { - userId: string - scopeId: number - - find(id: number): Promise - findEntries(...ids: number[]): Promise - list(): Promise -} \ No newline at end of file diff --git a/frontend/src/lib/database/mysql/database.ts b/frontend/src/lib/database/mysql/database.ts deleted file mode 100644 index 961016b..0000000 --- a/frontend/src/lib/database/mysql/database.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {createPool} from "mysql2/promise" -import type {Pool} from "mysql2/promise"; - -import type { Database, ScopeRepo, StatRepo } from "../interfaces"; -import MysqlDBScopes from "./scopes"; - - -export default class MysqlDB implements Database { - connection: Pool - userId: string; - - private constructor(userId: string, connection: Pool) { - this.userId = userId; - this.connection = connection; - } - - scopes(): ScopeRepo { - return new MysqlDBScopes(this.connection, this.userId); - } - stats(scopeId: number): StatRepo { - throw new Error("Method not implemented."); - } - - withUser(userId: string): Database { - return new MysqlDB(userId, this.connection); - } - - static async connectEnv(): Promise { - return this.connect( - process.env.STUFFLOG3_MYSQL_HOST, - parseInt(process.env.STUFFLOG3_MYSQL_PORT), - process.env.STUFFLOG3_MYSQL_USERNAME, - process.env.STUFFLOG3_MYSQL_PASSWORD, - process.env.STUFFLOG3_MYSQL_SCHEMA, - ) - } - - static async connect(host: string, port: number, user: string, password: string, database: string): Promise { - const connection = await createPool({ - host, user, database, password, port, - waitForConnections: true, - connectionLimit: 20, - queueLimit: 0, - }); - - return new MysqlDB("", connection); - } -} \ No newline at end of file diff --git a/frontend/src/lib/database/mysql/scopes.ts b/frontend/src/lib/database/mysql/scopes.ts deleted file mode 100644 index e13af2c..0000000 --- a/frontend/src/lib/database/mysql/scopes.ts +++ /dev/null @@ -1,84 +0,0 @@ -import type {Pool} from "mysql2/promise"; - -import type Scope from "$lib/models/scope"; -import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; -import type { ScopeRepo } from "../interfaces"; - -export default class MysqlDBScopes implements ScopeRepo { - userId: string; - connection: Pool; - - constructor(connection: Pool, userId: string) { - this.connection = connection; - this.userId = userId; - } - - async find(id: number): Promise { - const [[scopeRows], [projectRows], [statRows]] = await Promise.all([ - this.connection.execute(` - SELECT scope.*, scope_member.name as display_name - FROM scope - INNER JOIN scope_member ON scope.id = scope_member.scope_id - WHERE scope.id = ? - `, [id]), - this.connection.execute(` - SELECT id,name,status - FROM project - WHERE scope_id = ? - `, [id]).catch(() => []), - this.connection.execute(` - SELECT * - FROM stats - WHERE scope_id = ? - `, [id]).catch(() => []), - ]); - - const r = scopeRows[0] as any; - if (!r) { - return null; - } - - return { - id: r.id, - name: r.name, - abbreviation: r.abbreviation, - displayName: r.display_name, - projects: ((projectRows||[]) as any[]).map((p:any) => ({ - id: p.id, - name: p.name, - status: p.status, - })), - stats: ((statRows||[]) as any[]).map((s:any) => ({ - id: s.id, - name: s.name, - weight: s.weight, - description: s.description, - allowedAmounts: s.allowed_amounts || void(0), - })), - } - } - async list(): Promise { - const [rows] = await this.connection.execute(` - SELECT scope.*, scope_member.name as display_name - FROM scope - INNER JOIN scope_member ON id = scope_id - WHERE user_id = ? - `, [this.userId]); - - return (rows as any[]).map(r => ({ - id: r.id, - name: r.name, - abbreviation: r.abbreviation, - displayName: r.display_name, - })) - } - create(input: ScopeInput): Promise { - throw new Error("Method not implemented."); - } - update(id: number, input: Partial): Promise { - throw new Error("Method not implemented."); - } - delete(id: number): Promise { - throw new Error("Method not implemented."); - } -} \ No newline at end of file diff --git a/frontend/src/lib/models/item.ts b/frontend/src/lib/models/item.ts index 62f83e0..c8c6be6 100644 --- a/frontend/src/lib/models/item.ts +++ b/frontend/src/lib/models/item.ts @@ -4,12 +4,16 @@ import type { StatProgressEntry } from "./stat"; export default interface Item { id: number + scopeId: number + ownerId: string + projectId?: number + projectRequirementId?: number name: string description: string - createdTime: Date - createdBy: string - acquireTime?: Date + createdTime: string + acquireTime?: string scheduledDate?: string + stats: StatProgressEntry[] } diff --git a/frontend/src/lib/models/project.ts b/frontend/src/lib/models/project.ts index 2a9fe6e..ea77505 100644 --- a/frontend/src/lib/models/project.ts +++ b/frontend/src/lib/models/project.ts @@ -1,17 +1,17 @@ +import type Item from "./item"; import type { ScopeEntry } from "./scope"; import type { StatProgressEntry } from "./stat"; import type Status from "./status"; export default interface Project extends ProjectEntry { description: string - author: string - - scope: ScopeEntry requirements: Requirement[] } export interface ProjectEntry { id: number + ownerId: string + createdTime: string name: string status: Status } @@ -23,6 +23,7 @@ export interface Requirement { status: Status stats: StatProgressEntry[] + items: Item[] } export interface StandaloneRequirement extends Requirement { diff --git a/frontend/src/lib/models/scope.ts b/frontend/src/lib/models/scope.ts index 0a1deeb..9f13817 100644 --- a/frontend/src/lib/models/scope.ts +++ b/frontend/src/lib/models/scope.ts @@ -1,22 +1,18 @@ import type { ProjectEntry } from "./project"; import type Stat from "./stat"; - -export default interface Scope extends ScopeEntry { - projects: ProjectEntry[] - stats: Stat[] -} +import type Status from "./status"; export interface ScopeEntry { id: number name: string abbreviation: string - displayName: string } -export interface ScopeInput { - name: string - abbreviation: string +export default interface Scope extends ScopeEntry { displayName: string + projects: ProjectEntry[] + stats: Stat[] + statusLabels: Record } export interface ScopeMember { diff --git a/frontend/src/lib/models/stat.ts b/frontend/src/lib/models/stat.ts index ddbf114..b5f2175 100644 --- a/frontend/src/lib/models/stat.ts +++ b/frontend/src/lib/models/stat.ts @@ -18,35 +18,3 @@ export interface StatAggregate { acquired: number required: number } - -const EXAMPLE2: Stat = { - id: 12, - name: "Assets", - description: "The amount of 'assets' produced in the end. This does not contribute towards the weight", - weight: 0, -} - -const EXAMPLE3: Stat = { - id: 12, - name: "Hard Surface Practice", - description: "A value of the asset complexity, indicating how much general modeling practice is involved.", - weight: 0.5, - allowedAmounts: { - "Rote": 1, - "Novel": 2, - "Tutorial": 4, - "Difficult": 7, - } -} - -const EXAMPLE: Stat = { - id: 12, - name: "Complexity Points", - description: "A value of the asset complexity, indicating how much general modeling practice is involved.", - weight: 1, - allowedAmounts: { - "Small": 1, - "Moderate": 3, - "Complex": 7, - } -} \ No newline at end of file diff --git a/frontend/src/lib/utils/slugify.ts b/frontend/src/lib/utils/slugify.ts new file mode 100644 index 0000000..70c69aa --- /dev/null +++ b/frontend/src/lib/utils/slugify.ts @@ -0,0 +1,13 @@ +export default function sluggify(s: string): string { + let out = ""; + + for (const c of s.toLocaleLowerCase()) { + if (c >= 'a' && c <= 'z') { + out += c; + } else if (!":'".includes(c)) { + out += "-"; + } + } + + return out; +} diff --git a/frontend/src/routes/[scope].json.ts b/frontend/src/routes/[scope].json.ts deleted file mode 100644 index dd586d2..0000000 --- a/frontend/src/routes/[scope].json.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { RequestHandler } from "@sveltejs/kit"; - -import config from "$lib/config"; - -export const get: RequestHandler = async({params, locals}) => { - const scopeId = parseInt(params.scope); - - const db = await config.database(); - const scope = await db.withUser(locals.user.id).scopes().find(scopeId) - if (scope === null) { - return { - status: 404, - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - message: "Scope not found", - }), - } - } - - return { - status: 200, - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - scope - }), - } -} diff --git a/frontend/src/routes/[scope]/__layout.svelte b/frontend/src/routes/[scope]/__layout.svelte index 783496f..7e70ddd 100644 --- a/frontend/src/routes/[scope]/__layout.svelte +++ b/frontend/src/routes/[scope]/__layout.svelte @@ -2,22 +2,21 @@ import type { Load } from "@sveltejs/kit/types/internal"; export const load: Load = async({ fetch, params }) => { - const res = await fetch(`/${params.scope}.json`); + try { + const res: {scope: Scope} = await fetch(`/api/scopes/${params.scope.split("-")[0]}`).then(r => r.json()); - if (res.ok) { - const data = await res.json(); - - return { - props: { - ...data, + return { + props: { + ...res, }, stuff: { - scope: data.scope, + scope: res.scope, + scopePath: params.scope, } - }; - } + }; + } catch(_) {} - return {fallthrough: true}; + return {fallthrough: true} as any; } const STATUS_ORDER = [ @@ -38,6 +37,8 @@ import MenuCategory from "$lib/components/layout/MenuCategory.svelte"; import type { ProjectEntry } from "$lib/models/project"; import MenuItem from "$lib/components/layout/MenuItem.svelte"; + import {page} from "$app/stores"; +import sluggify from "$lib/utils/slugify"; export let scope: Scope; @@ -56,14 +57,14 @@
{scope.abbreviation}
- Overview - History + Overview + History {#each STATUS_ORDER as status (status)} {#if projectsByStatus[status].length > 0} {#each projectsByStatus[status] as project (project.id)} - { project.name.includes(": ") ? project.name.split(": ").slice(1).join(": ") : project.name diff --git a/frontend/src/routes/api/[...any]/index.ts b/frontend/src/routes/api/[...any]/index.ts new file mode 100644 index 0000000..237ba17 --- /dev/null +++ b/frontend/src/routes/api/[...any]/index.ts @@ -0,0 +1,30 @@ +import type { RequestHandler } from "@sveltejs/kit"; + +export const get: RequestHandler = async({ request, params }) => { + const proxyUrl = `${process.env.STUFFLOG3_API}/api/${params.any}`; + + console.log("PRX", proxyUrl); + const res = await fetch(proxyUrl, { + method: request.method, + headers: { + ...request.headers, + }, + body: request.body, + }); + + const result = { + status: res.status, + body: await res.text(), + headers: {}, + } + res.headers.forEach((v, k) => { + result.headers[k] = v; + }) + + return result; +} + +export const options = get; +export const post = get; +export const put = get; +export const del = get; \ No newline at end of file diff --git a/frontend/src/routes/indexdata.json.ts b/frontend/src/routes/indexdata.json.ts index 8d5e72d..67fe963 100644 --- a/frontend/src/routes/indexdata.json.ts +++ b/frontend/src/routes/indexdata.json.ts @@ -9,174 +9,9 @@ import type { StandaloneSprint } from "$lib/models/sprint" export const get: RequestHandler = async({locals}) => { const scopes: ScopeEntry[] = locals.scopes; - const items: StandaloneItem[] = [ - { - id: 1, scope: scopes[0], - name: "Table", - description: "A table for the Redrock rec-room.", - acquireDate: "2022-03-14T00:00:00Z", - createdDate: new Date(Date.now() - 3623511).toISOString(), - stats: [ - {id: 1, name: "Asset", weight: 0.2, required: 1, acquired: 1}, - {id: 2, name: "Complexity", weight: 1, required: 3, acquired: 5}, - ], - project: { - id: 443, - name: "3D Maps: Proving the concept", - status: Status.Active - } - }, - { - id: 2, scope: scopes[0], - name: "Datapad Set", - description: "I need one, at least, but more is better.", - createdDate: new Date(Date.now() - 3623511).toISOString(), - stats: [ - {id: 1, name: "Asset", weight: 0.2, required: 1, acquired: 0}, - {id: 2, name: "Complexity", weight: 1, required: 3, acquired: 0}, - {id: 3, name: "Hard Surface", weight: 0.333, required: 1, acquired: 0}, - ], - project: { - id: 443, - name: "3D Maps: Proving the concept", - status: Status.Active - } - }, - { - id: 3, scope: scopes[1], - name: "Enila/Renala: Technicalities", - description: "Renala is answering Leah's concern and takes the shift at the Respite.", - createdDate: new Date(Date.now() - 3623511).toISOString(), - stats: [ - {id: 1, name: "Story", weight: 3, required: 1, acquired: 0}, - {id: 2, name: "Story Word", weight: 0.002, required: 500, acquired: 0}, - ], - project: { - id: 1, - name: "Background Stories", - status: Status.Active - } - }, - ] - - const requirements: StandaloneRequirement[] = [ - { - id: 1, - name: "Basic Furniture", - description: "I need a lot of it.", - project: { - id: 443, - name: "3D Maps: Proving the concept", - status: Status.Active - }, - status: Status.Background, - stats: [ - {id: 1, name: "Asset", weight: 0.2, acquired: 8, required: 15}, - {id: 2, name: "Complexity", weight: 1, acquired: 69, required: 30}, - {id: 3, name: "Hard Surface", weight: 0.333, acquired: 20, required: 5}, - ], - scope: scopes[0], - }, - { - id: 2, - name: "Catching Up", - description: "Write a few stories to get back up to speed with less written characters.", - project: { - id: 1, - name: "Background Stories", - status: Status.Active - }, - status: Status.Completed, - stats: [ - {id: 1, name: "Story", weight: 3, required: 10, acquired: 3}, - {id: 2, name: "Story Word", weight: 0.002, required: 5000, acquired: 2173}, - {id: 3, name: "Worldbuilding", weight: 0.5, required: 5, acquired: 1}, - ], - scope: scopes[1], - }, - { - id: 3, - name: "Chapter 3", - description: "it's go time", - project: { - id: 1, - name: "Wenera's Job", - status: Status.Active - }, - status: Status.Available, - stats: [ - {id: 1, name: "Story", weight: 3, required: 5, acquired: 1}, - {id: 2, name: "Story Word", weight: 0.002, required: 4000, acquired: 759}, - ], - scope: scopes[1], - } - ] - - const sprints: StandaloneSprint[] = [ - { - id: 553, - name: "March Sprint", - description: "Hey kid, wanna model some trees?", - from: "2022-03-01T00:00:00+0200", - to: "2022-03-30T23:59:59+0200", - coarse: false, - kind: "stats", - scope: scopes[0], - timed: true, - stats: [ - {id: 1, name: "Asset", weight: 0, acquired: 13, required: 15}, - {id: 2, name: "Complexity", weight: 1, acquired: 37, required: 30}, - {id: 3, name: "Hard Surface", weight: 0.25, acquired: 8, required: 5}, - {id: 4, name: "UV Mapping", weight: 0.25, acquired: 3, required: 5}, - ], - items: [], - requirements: [], - aggregateRequired: 30, - aggregateName: "3D Modeling", - }, - { - id: 643, - name: "March Sprint", - description: "Crank out stories", - from: "2022-03-01T00:00:00+0200", - to: "2022-03-30T23:59:59+0200", - coarse: false, - kind: "requirements", - scope: scopes[1], - timed: true, - stats: [], - items: [], - requirements: [ - requirements[1], - requirements[2], - ], - aggregateRequired: 0, - aggregateName: "", - }, - { - id: 771, - name: "March Sprint", - description: "Do these", - from: "2022-03-01T00:00:00+0200", - to: "2022-03-30T23:59:59+0200", - coarse: false, - kind: "items", - scope: scopes[0], - timed: true, - stats: [ - {id: 1, name: "Asset", weight: 0.2, required: 2, acquired: 1}, - {id: 2, name: "Complexity", weight: 1, required: 8, acquired: 5}, - {id: 3, name: "Hard Surface", weight: 0.333, required: 1, acquired: 0}, - ], - items: [ - items[0], - items[1], - ], - requirements: [], - aggregateRequired: 0, - aggregateName: "", - } - ] + const items: StandaloneItem[] = [] + const requirements: StandaloneRequirement[] = [] + const sprints: StandaloneSprint[] = [] return { status: 200, diff --git a/internal/database/mysql/scopes.go b/internal/database/mysql/scopes.go index 0e83d84..984456c 100644 --- a/internal/database/mysql/scopes.go +++ b/internal/database/mysql/scopes.go @@ -32,6 +32,10 @@ func (r *scopeRepository) Find(ctx context.Context, id int, full bool) (*models. Name: scope.Name, Abbreviation: scope.Abbreviation, }, + Members: []models.ScopeMember{}, + Projects: []models.ProjectEntry{}, + Stats: []models.Stat{}, + StatusLabels: make(map[models.Status]string), } eg, ctx := errgroup.WithContext(ctx) diff --git a/internal/models/item.go b/internal/models/item.go index aa4623b..0fcbc87 100644 --- a/internal/models/item.go +++ b/internal/models/item.go @@ -5,7 +5,7 @@ import "time" type Item struct { ID int `json:"id"` ScopeID int `json:"scopeId"` - OwnerID string `json:"-"` + OwnerID string `json:"ownerId"` ProjectID *int `json:"projectId,omitempty"` ProjectRequirementID *int `json:"projectRequirementId,omitempty"` diff --git a/internal/models/scope.go b/internal/models/scope.go index a7bee1a..4a6a088 100644 --- a/internal/models/scope.go +++ b/internal/models/scope.go @@ -16,10 +16,10 @@ type Scope struct { ScopeEntry DisplayName string `json:"displayName"` - Members []ScopeMember `json:"members,omitempty"` - Projects []ProjectEntry `json:"projects,omitempty"` - Stats []Stat `json:"stats,omitempty"` - StatusLabels map[Status]string `json:"statusLabels,omitempty"` // Not stored as part of scope, but may be in the future. + Members []ScopeMember `json:"members"` + Projects []ProjectEntry `json:"projects"` + Stats []Stat `json:"stats"` + StatusLabels map[Status]string `json:"statusLabels"` // Not stored as part of scope, but may be in the future. } func (s *Scope) Member(id string) *ScopeMember {