• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IGVF-DACC / igvf-catalog / f04c4eff-3c13-4478-a29c-13ca879b262f

13 Nov 2025 08:02PM UTC coverage: 86.995% (+0.08%) from 86.919%
f04c4eff-3c13-4478-a29c-13ca879b262f

push

circleci

web-flow
DSERV-1086-schema-yaml (#559)

290 of 361 branches covered (80.33%)

Branch coverage included in aggregate %.

75 of 77 new or added lines in 14 files covered. (97.4%)

20 existing lines in 2 files now uncovered.

7443 of 8528 relevant lines covered (87.28%)

1.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/src/routers/datatypeRouters/nodes/genes_structure.ts
1
import { z } from 'zod'
1✔
2
import { db } from '../../../database'
1✔
3
import { QUERY_LIMIT } from '../../../constants'
1✔
4
import { publicProcedure } from '../../../trpc'
1✔
5
import { getDBReturnStatements, getFilterStatements, paramsFormatType, preProcessRegionParam } from '../_helpers'
1✔
6
import { descriptions } from '../descriptions'
1✔
7
import { commonNodesParamsFormat } from '../params'
1✔
8
import { TRPCError } from '@trpc/server'
1✔
9
import { findTranscriptsFromProteinSearch } from '../edges/transcripts_proteins'
1✔
10
import { getSchema } from '../schema'
1✔
11

1✔
12
const MAX_PAGE_SIZE = 500
1✔
13
const REGION_IDX = 'idx_zkd_start_end'
1✔
14

1✔
15
const QueryFormat = z.object({
1✔
16
  gene_id: z.string().trim().optional(),
1✔
17
  gene_name: z.string().trim().optional(),
1✔
18
  transcript_id: z.string().trim().optional(),
1✔
19
  transcript_name: z.string().trim().optional(),
1✔
20
  protein_id: z.string().trim().optional(),
1✔
21
  protein_name: z.string().trim().optional(),
1✔
22
  region: z.string().trim().optional()
1✔
23
}).merge(commonNodesParamsFormat)
1✔
24

1✔
25
const GeneStructureFormat = z.object({
1✔
26
  _id: z.string(),
1✔
27
  name: z.string(),
1✔
28
  chr: z.string(),
1✔
29
  start: z.number().nullable(),
1✔
30
  end: z.number().nullable(),
1✔
31
  strand: z.string(),
1✔
32
  type: z.string(),
1✔
33
  gene_id: z.string(),
1✔
34
  gene_name: z.string(),
1✔
35
  transcript_id: z.string(),
1✔
36
  transcript_name: z.string(),
1✔
37
  protein_id: z.string().nullish(),
1✔
38
  exon_number: z.string(),
1✔
39
  exon_id: z.string().nullable(),
1✔
40
  organism: z.string(),
1✔
41
  source: z.string(),
1✔
42
  version: z.string(),
1✔
43
  source_url: z.string()
1✔
44
})
1✔
45

1✔
46
let genesStructureSchema = getSchema('data/schemas/nodes/genes_structure.GencodeStructure.json')
1✔
47

1✔
48
function validateInput (fromGene: boolean, fromTranscript: boolean, fromProtein: boolean, fromRegion: boolean): void {
4✔
49
  // count the number of parameters that are defined
4✔
50
  const numParams = [fromGene, fromTranscript, fromProtein, fromRegion].filter(item => item).length
4✔
51
  if (numParams > 1) {
4✔
52
    throw new TRPCError({
1✔
53
      code: 'BAD_REQUEST',
1✔
54
      message: 'Please provide parameters from only one of the four categories: gene, transcript, protein or region'
1✔
55
    })
1✔
56
  }
1✔
57
}
4✔
58

1✔
59
export async function geneStructureSearch (input: paramsFormatType): Promise<any[]> {
4✔
60
  let useIndex = ''
4✔
61
  if (input.region !== undefined) {
4✔
62
    useIndex = `OPTIONS { indexHint: "${REGION_IDX}", forceIndexHint: true }`
1✔
63
  }
1✔
64

4✔
65
  let limit = QUERY_LIMIT
4✔
66
  if (input.limit !== undefined) {
4!
67
    limit = (input.limit as number <= MAX_PAGE_SIZE) ? input.limit as number : MAX_PAGE_SIZE
×
68
    delete input.limit
×
69
  }
×
70
  const page = input.page ?? 0
4!
71
  delete input.page
4✔
72
  if (input.organism === 'Mus musculus') {
4!
NEW
73
    genesStructureSchema = getSchema('data/schemas/nodes/mm_genes_structure.GencodeStructure.json')
×
74
  }
×
75
  const genesStructureCollectionName = genesStructureSchema.db_collection_name as string
4✔
76
  let fromGene = false
4✔
77
  let fromTranscript = false
4✔
78
  let fromProtein = false
4✔
79
  let fromRegion = false
4✔
80
  for (const key in input) {
4✔
81
    if (input[key] !== undefined) {
9✔
82
      if (key === 'gene_id' || key === 'gene_name') {
9✔
83
        fromGene = true
2✔
84
      } else if (key === 'transcript_id' || key === 'transcript_name') {
9✔
85
        fromTranscript = true
1✔
86
      } else if (key === 'protein_id' || key === 'protein_name') {
7✔
87
        fromProtein = true
1✔
88
      } else if (key === 'region') {
6✔
89
        fromRegion = true
1✔
90
      }
1✔
91
    }
9✔
92
  }
9✔
93
  validateInput(fromGene, fromTranscript, fromProtein, fromRegion)
4✔
94
  const preProcessed = preProcessRegionParam(input)
4✔
95
  let filterBy = ''
4✔
96
  const filterSts = getFilterStatements(genesStructureSchema, preProcessed)
4✔
97
  if (filterSts !== '') {
4✔
98
    filterBy = `FILTER ${filterSts}`
2✔
99
  }
2✔
100
  if (!fromProtein) {
3✔
101
    const query = `
2✔
102
      FOR record in ${genesStructureCollectionName} ${useIndex}
2✔
103
      ${filterBy}
2✔
104
      SORT record.gene_id, record.transcript_id, record.start
2✔
105
      LIMIT ${page as number * limit}, ${limit}
2✔
106
      RETURN {${getDBReturnStatements(genesStructureSchema)}}
2✔
107
    `
2✔
108
    return await (await db.query(query)).all()
4✔
109
  } else {
4✔
110
    const proteinInput = {
4✔
111
      protein_id: input.protein_id,
4✔
112
      protein_name: input.protein_name,
4✔
113
      organism: input.organism,
4✔
114
      page: 0
4✔
115
    }
4✔
116
    const proteinsTranscripts = await findTranscriptsFromProteinSearch(proteinInput)
4✔
117
    const transcriptsList = proteinsTranscripts.map((item: any) => {
4✔
118
      return {
1✔
119
        transcript: item.transcript.split('/')[1],
1✔
120
        protein: item.protein.split('/')[1]
1✔
121
      }
1✔
122
    })
4✔
123
    const query = `
4✔
124
      FOR doc in ${JSON.stringify(transcriptsList)}
4✔
125
      FOR record in ${genesStructureCollectionName} ${useIndex}
4✔
126
      filter record.transcript_id == doc.transcript
4✔
127
      SORT record.gene_id, record.transcript_id, record.start
4✔
128
      LIMIT ${page as number * limit}, ${limit}
4✔
129
      RETURN {
4✔
130
        'protein_id': doc.protein,
4✔
131
      ${getDBReturnStatements(genesStructureSchema)}}
4✔
132
    `
4✔
133
    return await (await db.query(query)).all()
4✔
134
  }
4✔
135
}
4✔
136

1✔
137
const genesStructure = publicProcedure
1✔
138
  .meta({ openapi: { method: 'GET', path: '/genes-structure', description: descriptions.genes_structure } })
1✔
139
  .input(QueryFormat)
1✔
140
  .output(z.array(GeneStructureFormat))
1✔
141
  .query(async ({ input }) => await geneStructureSearch(input))
1✔
142

1✔
143
export const genesStructureRouters = {
1✔
144
  genesStructure
1✔
145
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc