Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | 195x 195x 65x 60x 2x 1x 1x 1x 65x 17x 15x 7x 7x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import { http, HttpResponse } from 'msw'
import { mockProjects } from '@/msw/mock-data'
interface StoredProject {
id: string
name: string
description: string | null
config: Record<string, unknown> | null
secrets: Record<string, unknown> | null
pipeline_count: number
created_by: string
updated_by: string
created_at: string
updated_at: string
}
function normalizeMock(p: Record<string, unknown>, idx: number): StoredProject {
return {
id: (p.id as string) ?? `proj-${idx}`,
name: (p.name as string) ?? 'Unnamed',
description: (p.description as string) ?? null,
config: (p.config as Record<string, unknown>) ?? null,
secrets: null,
pipeline_count: (p.pipeline_count as number) ?? 0,
created_by: (p.created_by as string) ?? 'user-1',
updated_by: (p.updated_by as string) ?? 'user-1',
created_at: (p.created_at as string) ?? new Date(Date.now() - 60 * 24 * 3600000).toISOString(),
updated_at: (p.updated_at as string) ?? new Date().toISOString(),
}
}
let projectStore: StoredProject[] = mockProjects.map((p, i) => normalizeMock(p as Record<string, unknown>, i + 1))
let idCounter = 10
export function resetProjectStore() {
projectStore = mockProjects.map((p, i) => normalizeMock(p as Record<string, unknown>, i + 1))
idCounter = 10
}
function toResponseProject(p: StoredProject): Omit<StoredProject, 'secrets'> {
return {
id: p.id,
name: p.name,
description: p.description,
config: p.config,
pipeline_count: p.pipeline_count,
created_by: p.created_by,
updated_by: p.updated_by,
created_at: p.created_at,
updated_at: p.updated_at,
E }
}
function jsonObjectTypeName(value: unknown): string {
if (Array.isArray(value)) {
return '[]interface {}'
}
switch (typeof value) {
case 'string':
return 'string'
case 'number':
return 'float64'
case 'boolean':
return 'bool'
default:
return typeof value
I}
}
function validateJSONObject(value: unknown, fieldName: string): string | null {
if (value === undefined || value === null) {
return null
}
Iif (typeof value === 'object' && !Array.isArray(value)) {
return null
}
return `${fieldName} must be a JSON object, not ${jsonObjectTypeName(value)}`
}I
export const projectHandlers = [
http.get('/api/v1/projects', () =>
I HttpResponse.json({
data: projectStore.map(toResponseProject),
meta: { limit: 50, returned: projectStore.length, has_more: false },
}),
),
http.get('/api/v1/projects/:id', ({ params }) => {
const project = projectStore.find((p) => p.id === params.id)
if (!project) {
return new HttpResponse(null, { status: 404 })
}
return HttpResponse.json(toResponseProject(project))
}),
http.post('/api/v1/projects', async ({ request }) => {
const body = (await request.json()) as {
name: string
description?: string | null
config?: unknown
secrets?: unknown
}
if (!body.name || body.name.length < 3 || body.name.length > 100) {
return HttpResponse.json(
{ error: 'Project name must be 3 to 100 characters' },
{ status: 400 },
)
}
const configError = validateJSONObject(body.config, 'config')
if (configError) {
return HttpResponse.json({ error: configError }, { status: 422 })
}
const secretsError = validateJSONObject(body.secrets, 'secrets')
if (secretsError) {
return HttpResponse.json({ error: secretsError }, { status: 422 })
}
const now = new Date().toISOString()
idCounter++
const newProject: StoredProject = {
id: `proj-${idCounter}`,
name: body.name,
description: body.description ?? null,
config: (body.config as Record<string, unknown>) ?? null,
secrets: (body.secrets as Record<string, unknown>) ?? null,
pipeline_count: 0,
created_by: 'user-1',
updated_by: 'user-1',
created_at: now,
updated_at: now,
}
projectStore.push(newProject)
return HttpResponse.json(toResponseProject(newProject), { status: 201 })
}),
http.put('/api/v1/projects/:id', async ({ params, request }) => {
const body = (await request.json()) as {
name?: string
description?: string | null
config?: unknown
secrets?: unknown
}
const existing = projectStore.find((p) => p.id === params.id)
if (!existing) {
return new HttpResponse(null, { status: 404 })
}
if (body.name !== undefined && (body.name.length < 3 || body.name.length > 100)) {
return HttpResponse.json(
{ error: 'Project name must be 3 to 100 characters' },
{ status: 400 },
)
}
const configError = validateJSONObject(body.config, 'config')
if (configError) {
return HttpResponse.json({ error: configError }, { status: 422 })
}
const secretsError = validateJSONObject(body.secrets, 'secrets')
if (secretsError) {
return HttpResponse.json({ error: secretsError }, { status: 422 })
}
Object.assign(existing, {
name: body.name ?? existing.name,
description: body.description !== undefined ? body.description : existing.description,
config: body.config ?? existing.config,
secrets: body.secrets ?? existing.secrets,
updated_at: new Date().toISOString(),
updated_by: 'user-1',
})
return HttpResponse.json(toResponseProject(existing))
}),
http.delete('/api/v1/projects/:id', ({ params }) => {
const idx = projectStore.findIndex((p) => p.id === params.id)
if (idx === -1) {
return new HttpResponse(null, { status: 404 })
}
projectStore.splice(idx, 1)
return new HttpResponse(null, { status: 204 })
}),
]
|