All files / layers / L1 / contracts projects.ts

80.95% Statements 17/21
100% Branches 8/8
50% Functions 4/8
80% Lines 16/20

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                                                    4x         9x   9x 9x 5x 1x   4x       9x 9x 4x 1x   3x       9x               3x                     3x 6x      
import { load } from 'js-yaml'
import type { ApiContract } from '@/layers/interfaces'
import type { components } from '@/types/api'
 
type Project = components['schemas']['Project']
type CreateProjectRequest = components['schemas']['CreateProjectRequest']
type UpdateProjectRequest = components['schemas']['UpdateProjectRequest']
 
// Form-side bodies: generated request types now carry the API object shapes
// (config, secrets), but the UI form diverges — secrets arrives as a plain
// string (password field) and config as a YAML string or a JSON object.
type ProjectCreateBody = Omit<Partial<CreateProjectRequest>, 'config' | 'secrets'> & {
  config?: string | Record<string, unknown> | null
  secrets?: string
}
type ProjectUpdateBody = Omit<Partial<UpdateProjectRequest>, 'config' | 'secrets'> & {
  config?: string | Record<string, unknown> | null
  secrets?: string
}
 
type ProjectListQuery = {
  [key: string]: string | number | boolean | undefined
}
 
type JsonObject = Record<string, unknown>
 
const webhookSecrets = (value: string): JsonObject => ({
  webhooks: { github_pr: { webhook_secret: value } },
})
 
function normalizeBody(input: JsonObject): JsonObject {
  const body = { ...input }
 
  const secrets = body['secrets']
  if (typeof secrets === 'string') {
    if (secrets === '') {
      delete body['secrets']
    } else {
      body['secrets'] = webhookSecrets(secrets)
    }
  }
 
  const config = body['config']
  if (typeof config === 'string') {
    if (config === '') {
      delete body['config']
    } else {
      body['config'] = load(config)
    }
  }
 
  return body
}
 
export const projectsContract: ApiContract<
  Project,
  ProjectCreateBody,
  ProjectUpdateBody,
  ProjectListQuery
> = {
  idOf: (e) => e.id,
  entityPath: 'projects',
  endpoints: {
    list: '/projects',
    detail: (id) => `/projects/${id}`,
    create: '/projects',
    update: (id) => `/projects/${id}`,
    remove: (id) => `/projects/${id}`,
  },
  request: {
    create: (input) => normalizeBody(input as JsonObject) as ProjectCreateBody,
    update: (input) => normalizeBody(input as JsonObject) as ProjectUpdateBody,
  },
}