Skip to content

Commit 9ee41af

Browse files
authored
Merge pull request #598 from chetanr25/contract/template-layout
Templates contract update: field layout and PDF upload
2 parents e8d7459 + ae550fc commit 9ee41af

3 files changed

Lines changed: 226 additions & 26 deletions

File tree

contracts/openapi.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ paths:
8686
$ref: "path/templates.yaml#/template_by_id"
8787
/api/v1/templates/{template_id}/fields:
8888
$ref: "path/templates.yaml#/template_fields"
89+
/api/v1/templates/pdf:
90+
$ref: "path/templates.yaml#/templates_pdf"
8991

9092
# ── Layer 7: System ────────────────────────────────────────────
9193
/api/v1/health:

contracts/path/templates.yaml

Lines changed: 151 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# POST /api/v1/templates
55
# PUT /api/v1/templates/{template_id}
66
# GET /api/v1/templates/{template_id}/fields
7+
# POST /api/v1/templates/pdf
78

89
templates:
910
get:
@@ -13,7 +14,7 @@ templates:
1314
Returns all registered form templates including built-in standard templates
1415
(NERIS, NEMSIS, NIBRS, NFIRS modules, OSHA, etc.) and any custom templates
1516
added for specific jurisdictions. Each template defines the fields, validation
16-
rules, and mapping from the canonical FireForm schema.
17+
rules, and mapping from the FireForm incident schema.
1718
tags:
1819
- templates
1920
responses:
@@ -61,7 +62,7 @@ templates:
6162
Registers a new form template for a jurisdiction or agency not yet supported.
6263
This is how FireForm extends to new states, countries, or custom agency forms
6364
without code changes. The template defines all fields, their types, validation
64-
rules, and how each maps from the canonical FireForm schema.
65+
rules, and how each maps from the FireForm incident schema.
6566
tags:
6667
- templates
6768
requestBody:
@@ -75,13 +76,24 @@ templates:
7576
display_name: "Texas State Fire Marshal Incident Report"
7677
jurisdiction: "US-TX"
7778
agency_type: "fire_department"
79+
pdf_template_ref: "templates/state_texas.pdf"
7880
fields:
7981
- field_name: "incident_number"
8082
field_type: "string"
8183
required: true
8284
max_length: 20
8385
description: "State-assigned incident number"
84-
canonical_mapping: "report_metadata.incident_number"
86+
incident_mapping: "report_metadata.incident_number"
87+
layout:
88+
page: 0
89+
x: 188.33
90+
y: 621.33
91+
width: 127.33
92+
height: 28.67
93+
font: "Helvetica"
94+
font_size: 10
95+
color: "#000000"
96+
align: "left"
8597
- field_name: "fire_cause"
8698
field_type: "enum"
8799
required: true
@@ -90,10 +102,26 @@ templates:
90102
- "natural"
91103
- "intentional"
92104
- "undetermined"
93-
canonical_mapping: "fire.cause_category"
94-
field_mappings_from_canonical:
95-
"report_metadata.incident_number": "incident_number"
96-
"fire.cause_category": "fire_cause"
105+
incident_mapping: "fire.cause_category"
106+
layout:
107+
page: 0
108+
x: 188.33
109+
y: 560.0
110+
width: 200.0
111+
height: 18.0
112+
- field_name: "report_footer"
113+
field_type: "string"
114+
required: false
115+
static_text: "Generated by FireForm"
116+
incident_mapping: null
117+
layout:
118+
page: 0
119+
x: 72.0
120+
y: 40.0
121+
width: 300.0
122+
height: 12.0
123+
font_size: 8
124+
align: "center"
97125
responses:
98126
"201":
99127
description: Template created
@@ -120,8 +148,8 @@ template_by_id:
120148
summary: Get full template schema
121149
description: |
122150
Returns the complete template definition including all fields, their types,
123-
required/optional status, validation rules, and the mapping from canonical
124-
FireForm schema fields. Includes the source standard reference where applicable.
151+
required/optional status, validation rules, and the mapping from the FireForm
152+
incident schema fields. Includes the source standard reference where applicable.
125153
tags:
126154
- templates
127155
parameters:
@@ -201,7 +229,7 @@ template_fields:
201229
summary: Get template field definitions
202230
description: |
203231
Returns just the fields list for a template with type, required/optional
204-
status, validation rules, and canonical schema mapping. Useful for building
232+
status, validation rules, and incident-schema mapping. Useful for building
205233
validation checklists and for the validate endpoint to determine requirements.
206234
tags:
207235
- templates
@@ -253,15 +281,126 @@ template_fields:
253281
field_type: "enum"
254282
required: true
255283
description: "Primary incident type code"
256-
canonical_mapping: "incident.types[0].neris_code"
284+
incident_mapping: "incident.types[0].neris_code"
285+
layout:
286+
page: 0
287+
x: 120.0
288+
y: 680.0
289+
width: 160.0
290+
height: 18.0
257291
- field_name: "incident_date"
258292
field_type: "date"
259293
required: true
260294
description: "Date of incident"
261-
canonical_mapping: "incident.start_datetime"
295+
incident_mapping: "incident.start_datetime"
296+
layout:
297+
page: 0
298+
x: 120.0
299+
y: 655.0
300+
width: 120.0
301+
height: 18.0
262302
"404":
263303
description: Template not found
264304
content:
265305
application/json:
266306
schema:
267307
$ref: "../schemas/common.yaml#/ErrorResponse"
308+
309+
templates_pdf:
310+
post:
311+
operationId: uploadTemplatePdf
312+
summary: Upload a blank PDF for a template before defining its fields
313+
description: |
314+
Uploads the blank agency PDF that a template's field coordinates will be
315+
written onto, and returns a `pdf_template_ref` plus the page geometry.
316+
317+
This is the first step of the template-authoring flow. The visual editor
318+
renders the returned pages, the user draws a box per field, and the box
319+
coordinates become each field's `layout`. The resulting `pdf_template_ref`
320+
is then sent in the `POST /api/v1/templates` body. Upload precedes create
321+
because the layout coordinates only have meaning relative to this PDF.
322+
323+
Page dimensions are returned in PDF points (1/72 inch, origin bottom-left)
324+
so the editor can map canvas positions to the same coordinate system the
325+
`layout` fields use.
326+
tags:
327+
- templates
328+
requestBody:
329+
required: true
330+
content:
331+
multipart/form-data:
332+
schema:
333+
type: object
334+
required:
335+
- pdf_file
336+
properties:
337+
pdf_file:
338+
type: string
339+
format: binary
340+
description: Blank fillable or flat PDF form. Max 50MB.
341+
responses:
342+
"201":
343+
description: PDF stored and ready to be referenced by a template
344+
content:
345+
application/json:
346+
schema:
347+
type: object
348+
required:
349+
- pdf_template_ref
350+
- original_filename
351+
- page_count
352+
- pages
353+
properties:
354+
pdf_template_ref:
355+
type: string
356+
description: Opaque reference to the stored PDF, passed back in
357+
the template body as pdf_template_ref
358+
original_filename:
359+
type: string
360+
page_count:
361+
type: integer
362+
pages:
363+
type: array
364+
description: Per-page geometry in PDF points, index 0 = first page
365+
items:
366+
type: object
367+
required:
368+
- page
369+
- width
370+
- height
371+
properties:
372+
page:
373+
type: integer
374+
width:
375+
type: number
376+
height:
377+
type: number
378+
example:
379+
pdf_template_ref: "templates/uploads/550e8400-e29b-41d4-a716-446655440080.pdf"
380+
original_filename: "texas_sfm_incident.pdf"
381+
page_count: 2
382+
pages:
383+
- page: 0
384+
width: 612.0
385+
height: 792.0
386+
- page: 1
387+
width: 612.0
388+
height: 792.0
389+
"400":
390+
description: Missing file or malformed multipart request
391+
content:
392+
application/json:
393+
schema:
394+
$ref: "../schemas/common.yaml#/ErrorResponse"
395+
"413":
396+
description: PDF exceeds the maximum allowed size
397+
content:
398+
application/json:
399+
schema:
400+
$ref: "../schemas/common.yaml#/ErrorResponse"
401+
"415":
402+
description: Uploaded file is not a valid PDF
403+
content:
404+
application/json:
405+
schema:
406+
$ref: "../schemas/common.yaml#/ErrorResponse"

contracts/schemas/template.yaml

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@ TemplateSummary:
77
type: string
88
format: uuid
99
form_type:
10-
$ref: "../schemas/enums.yaml#/FormType"
10+
type: string
11+
description: Unique form type identifier (built-in or custom jurisdiction)
1112
display_name:
1213
type: string
1314
jurisdiction:
1415
type: string
16+
nullable: true
1517
description: Jurisdiction code (e.g. "US-Federal", "US-CA", "US-GA")
1618
agency_type:
1719
type: string
20+
nullable: true
1821
version:
1922
type: string
2023
last_updated:
@@ -34,9 +37,7 @@ CreateTemplateRequest:
3437
required:
3538
- form_type
3639
- display_name
37-
- jurisdiction
3840
- fields
39-
- field_mappings_from_canonical
4041
properties:
4142
form_type:
4243
type: string
@@ -45,27 +46,24 @@ CreateTemplateRequest:
4546
type: string
4647
jurisdiction:
4748
type: string
49+
nullable: true
50+
description: Jurisdiction code. Optional — the visual editor may register a
51+
template before jurisdiction is assigned.
4852
agency_type:
4953
type: string
54+
nullable: true
5055
fields:
5156
type: array
5257
items:
5358
$ref: "#/TemplateField"
54-
field_mappings_from_canonical:
55-
type: object
56-
additionalProperties:
57-
type: string
58-
description: |
59-
Mapping from canonical FireForm JSON paths to this template's field names.
60-
Key = canonical path (e.g. "fire.cause_category"), value = template field name.
6159
source_standard:
6260
type: string
6361
nullable: true
6462
description: Reference to the source standard (e.g. "NERIS v2.0", "NFIRS 5.0")
6563
pdf_template_ref:
6664
type: string
6765
nullable: true
68-
description: Reference to the PDF template file
66+
description: Reference to the PDF template file the layout coordinates apply to
6967

7068
Template:
7169
allOf:
@@ -136,9 +134,70 @@ TemplateField:
136134
type: string
137135
nullable: true
138136
description: Valid values for enum fields
139-
canonical_mapping:
137+
incident_mapping:
140138
type: string
141-
description: JSON path in canonical FireForm schema this field maps from
139+
nullable: true
140+
description: |
141+
JSON path in the FireForm incident schema this field pulls its value from
142+
(e.g. "fire.cause_category"). Null for a static field. Exactly one of
143+
incident_mapping or static_text is expected.
144+
static_text:
145+
type: string
146+
nullable: true
147+
description: |
148+
Fixed text drawn into the field instead of a mapped value. Null for a
149+
data-mapped field. Mutually exclusive with incident_mapping.
142150
default_value:
143151
nullable: true
144-
description: Default value if canonical field is null
152+
description: Default value if the mapped incident field is null
153+
layout:
154+
nullable: true
155+
description: Visual placement of the field on the PDF. Null for fields with
156+
no fixed position.
157+
allOf:
158+
- $ref: "#/TemplateFieldLayout"
159+
160+
TemplateFieldLayout:
161+
type: object
162+
description: |
163+
Visual placement of a field on the PDF page. Coordinates are in PDF points
164+
with the origin at the bottom-left of the page, so the field box runs from
165+
start = (x, y) to end = (x + width, y + height). The editor is responsible
166+
for converting rendered-canvas pixels to PDF points before saving.
167+
required:
168+
- page
169+
- x
170+
- y
171+
- width
172+
- height
173+
properties:
174+
page:
175+
type: integer
176+
description: Zero-based page index
177+
x:
178+
type: number
179+
description: Lower-left X of the box, in PDF points
180+
y:
181+
type: number
182+
description: Lower-left Y of the box, in PDF points (origin bottom-left)
183+
width:
184+
type: number
185+
height:
186+
type: number
187+
font:
188+
type: string
189+
default: Helvetica
190+
font_size:
191+
type: number
192+
default: 10
193+
color:
194+
type: string
195+
default: "#000000"
196+
description: Hex color, e.g. "#000000"
197+
align:
198+
type: string
199+
enum:
200+
- left
201+
- center
202+
- right
203+
default: left

0 commit comments

Comments
 (0)