Task
Create the stub CSAPIQueryBuilder class in src/ogc-api/csapi/url_builder.ts with constructor, private helper methods, resource validation, and 1-2 proof-of-concept public methods, plus its test file src/ogc-api/csapi/url_builder.spec.ts.
ROADMAP Reference: Phase 1, Task 3 — Create Stub QueryBuilder (~3-4 hours, Low complexity)
Files to Create or Modify
| File |
Action |
Est. Lines |
Purpose |
src/ogc-api/csapi/url_builder.ts |
Create |
~80-120 |
Stub QueryBuilder class with constructor, 3 private helpers, availableResources property, and 1-2 public methods |
src/ogc-api/csapi/url_builder.spec.ts |
Create |
~100-150 |
Tests for constructor, resource validation, and the 1-2 public methods |
Blueprint Reference
Follow the EDR QueryBuilder pattern established in src/ogc-api/edr/url_builder.ts (562 lines). Key patterns to match:
- Class structure:
export default class with constructor accepting OgcApiCollectionInfo
- Constructor: Extracts capabilities from collection info, stores as instance properties
- Private fields: Use
private collection_ trailing underscore convention (matches EDR's private collection)
- Import style: Named imports from
../model.js and ./model.js with .js extensions
- JSDoc: Class-level
@see link to specification
EDR has no url_builder.spec.ts — this will be a new test file. Follow test structure from src/ogc-api/edr/helpers.spec.ts (46 lines) and src/ogc-api/edr/model.spec.ts (39 lines) for describe/it patterns.
Scope — What to Implement
Class Structure
export default class CSAPIQueryBuilder {
public readonly availableResources: Set<string>;
constructor(private collection_: OgcApiCollectionInfo) {
this.availableResources = this.extractAvailableResources();
}
}
Private Helper Methods (3 methods)
-
private extractAvailableResources(): Set<string> — Resource discovery from collection links
- Parse collection
links array for CSAPI resource type indicators (e.g., rel matching ogc-cs:{resourceType})
- Return a
Set<string> of available resource type names (e.g., 'systems', 'datastreams')
- Called once in constructor
-
private buildResourceUrl(resourceType: string, id?: string, subPath?: string, options?: QueryOptions): string — Core URL construction using this.baseUrl
- Canonical:
{baseUrl}/{resourceType} → /systems
- By ID:
{baseUrl}/{resourceType}/{id} → /systems/abc123
- Nested:
{baseUrl}/{resourceType}/{id}/{subPath} → /systems/abc123/subsystems
- Appends query string via
buildQueryString()
-
private buildQueryString(options?: QueryOptions): string — Parameter serialization with encoding
- Convert query options object to URL query string
- Handle
undefined/null values (skip them)
- Handle array parameters (e.g.,
id=sys1,sys2,sys3 as comma-joined)
- Return empty string when no options,
?key=val&... when options present
Public Property
public readonly availableResources: Set<string> — Exposes discovered resource types for user inspection
Resource Validation Pattern
All public methods validate resource availability before building URLs (~2 lines per method):
if (!this.availableResources.has('systems')) {
throw new EndpointError(
`Collection '${this.collection_.id}' does not support 'systems' resource. ` +
`Available resources: ${Array.from(this.availableResources).join(', ')}`
);
}
Proof-of-Concept Public Methods (1-2 methods)
Implement 1-2 simple public methods as proof of concept using the private helpers:
getSystems(options?: SystemQueryOptions): string — Returns URL for listing systems
getSystem(id: string, options?: QueryOptions): string — Returns URL for a single system by ID
These methods demonstrate the complete pattern: validate → build URL → return string. All 78 remaining methods (Phase 2, issues #5-#13) follow this exact pattern.
JSDoc Requirements
- Class-level JSDoc with
@see link to CSAPI Part 1 specification
- Document constructor with parameter description
- Document public
availableResources property
- Document each public method with parameter descriptions, return type, and usage example
- Private helpers get internal JSDoc only (not part of public API docs)
- Follow the JSDoc style in
src/ogc-api/edr/url_builder.ts
Testing Requirements
- Create
src/ogc-api/csapi/url_builder.spec.ts (~100-150 lines)
- Constructor tests:
- Constructs successfully with valid collection info containing CSAPI links
- Populates
availableResources from collection links
- Handles collection with no CSAPI links (empty
availableResources)
- Resource validation tests:
- Public method throws
EndpointError when resource type is unavailable
- Error message includes collection ID and lists available resources
- Public method succeeds when resource type is available
- Public method tests:
getSystems() returns correct URL with no options
getSystems({ limit: 10 }) returns correct URL with query parameters
getSystem('abc123') returns correct URL with resource ID
getSystems({ bbox: [-180, -90, 180, 90] }) returns correct URL with bbox parameter
- Query string tests:
- Properly encodes special characters in parameter values
- Skips
undefined/null values
- Joins array parameters with commas
- Follow test patterns from
src/ogc-api/edr/helpers.spec.ts and src/ogc-api/edr/model.spec.ts
- Use
globalThis.fetch = jest.fn() if HTTP mocking is needed (unlikely for this stub)
Scope — What NOT to Touch
Acceptance Criteria
Dependencies
Blocked by: #1 — Phase 1.1: Create Type System (QueryBuilder imports types from model.ts), #2 — Phase 1.2: Create Helper Utilities (QueryBuilder may use temporal encoding and validation utilities from helpers.ts)
Blocks: #4 — Phase 1.4: Integrate with OgcApiEndpoint (integration imports CSAPIQueryBuilder), #5-#13 — Phase 2: All resource method implementations build on this stub
Operational Constraints
⚠️ MANDATORY: Before starting work on this issue, review docs/governance/AI_OPERATIONAL_CONSTRAINTS.md.
Key constraints for this task:
- Precedence: OGC specifications → AI Collaboration Agreement → This issue description → Existing code → Conversational context
- No scope expansion: Do not infer unstated requirements or add unrequested features
- No refactoring: Do not rename, restructure, or "improve" code outside this issue's scope
- Minimal diffs: Prefer the smallest change that satisfies the acceptance criteria
- Ask when unclear: If intent is ambiguous, stop and ask for clarification
References
Read these documents before starting implementation. They are ordered by priority.
Primary References (must read)
| # |
Document |
Section/Lines |
What It Provides |
| 1 |
docs/planning/csapi-implementation-guide.md §6: "Resource Validation Strategy" |
Lines 535-600 |
Complete constructor and validation pattern — exact code for constructor, availableResources, and per-method validation with EndpointError |
| 2 |
docs/planning/csapi-implementation-guide.md §6: "Helper Methods" |
Lines 620-720 |
Complete private helper method specifications — buildResourceUrl(), buildQueryString(), extractAvailableResources() with full code blocks showing signatures, implementation logic, and link-parsing pattern |
| 3 |
src/ogc-api/edr/url_builder.ts |
Full file (562 lines) |
Upstream blueprint — the accepted pattern for a QueryBuilder class. Match class structure, constructor pattern, export default class, import style, and JSDoc format |
| 4 |
src/ogc-api/edr/helpers.spec.ts |
Full file (46 lines) |
Test blueprint — the accepted describe/it structure and assertion style for test files |
Upstream Type/Import References (files this task imports from)
| # |
Document |
What to Import |
| 5 |
src/ogc-api/csapi/model.ts (created in issue #1) |
SystemQueryOptions, QueryOptions, CSAPIResourceType |
| 6 |
src/ogc-api/model.ts |
OgcApiCollectionInfo (collection metadata type passed to constructor) |
| 7 |
src/ogc-api/csapi/helpers.ts (created in issue #2) |
Temporal encoding and validation utilities if needed by buildQueryString() |
| 8 |
src/shared/models.ts |
EndpointError (for validation error messages) |
Research References (context, not required reading)
| # |
Document |
What It Provides |
| 9 |
docs/research/upstream/querybuilder-pattern-analysis.md |
Analysis of QueryBuilder patterns — why single class with private helpers, not multi-class with inheritance |
| 10 |
docs/research/upstream/url-building-analysis.md |
URL construction patterns across all upstream APIs — why buildResourceUrl uses this specific signature |
| 11 |
docs/research/design/csapiquerybuilder/architecture-decision/results/DECISION-part1-structure.md |
Complete structural design with confidence ratings — why helper methods, why resource validation |
| 12 |
docs/research/design/csapiquerybuilder/architecture-decision/results/DECISION-part2-implementation.md |
Implementation details — exact code patterns for constructor and helpers |
| 13 |
docs/planning/ROADMAP.md Phase 1, Task 3 |
Lines 91-103 — task description with time estimates and implementation notes |
Specification References (for @see links in JSDoc)
| # |
Document |
Use |
| 14 |
OGC API - Connected Systems Part 1 (23-001) |
Canonical resource endpoint paths (/systems, /deployments, etc.) for URL construction |
| 15 |
OGC API - Connected Systems Part 2 (23-002) |
Part 2 resource endpoint paths (/datastreams, /observations, etc.) |
| 16 |
docs/research/standards/ogcapi-connectedsystems-1.bundled.oas31.yaml |
Part 1 OpenAPI schema — canonical endpoint definitions |
| 17 |
docs/research/standards/ogcapi-connectedsystems-2.bundled.oas31.yaml |
Part 2 OpenAPI schema — query parameter definitions |
Convention Quick Reference
| Rule |
Example |
Use .js extension for relative imports |
import { OgcApiCollectionInfo } from '../model.js' |
Use import type for interfaces/types |
import type { SystemQueryOptions } from './model.js' |
export default class for QueryBuilder |
export default class CSAPIQueryBuilder { ... } |
| Trailing underscore for private constructor params |
constructor(private collection_: OgcApiCollectionInfo) |
| Named exports for types and utilities |
export interface QueryOptions { ... } |
HTTP mocking: globalThis.fetch = jest.fn() |
Probably not needed for this stub (no HTTP calls) |
| Meaningful tests only |
Verify actual URL output, validation error messages — not just that code runs without throwing |
Task
Create the stub CSAPIQueryBuilder class in
src/ogc-api/csapi/url_builder.tswith constructor, private helper methods, resource validation, and 1-2 proof-of-concept public methods, plus its test filesrc/ogc-api/csapi/url_builder.spec.ts.ROADMAP Reference: Phase 1, Task 3 — Create Stub QueryBuilder (~3-4 hours, Low complexity)
Files to Create or Modify
src/ogc-api/csapi/url_builder.tsavailableResourcesproperty, and 1-2 public methodssrc/ogc-api/csapi/url_builder.spec.tsBlueprint Reference
Follow the EDR QueryBuilder pattern established in
src/ogc-api/edr/url_builder.ts(562 lines). Key patterns to match:export default classwith constructor acceptingOgcApiCollectionInfoprivate collection_trailing underscore convention (matches EDR'sprivate collection)../model.jsand./model.jswith.jsextensions@seelink to specificationEDR has no
url_builder.spec.ts— this will be a new test file. Follow test structure fromsrc/ogc-api/edr/helpers.spec.ts(46 lines) andsrc/ogc-api/edr/model.spec.ts(39 lines) fordescribe/itpatterns.Scope — What to Implement
Class Structure
Private Helper Methods (3 methods)
private extractAvailableResources(): Set<string>— Resource discovery from collection linkslinksarray for CSAPI resource type indicators (e.g.,relmatchingogc-cs:{resourceType})Set<string>of available resource type names (e.g.,'systems','datastreams')private buildResourceUrl(resourceType: string, id?: string, subPath?: string, options?: QueryOptions): string— Core URL construction usingthis.baseUrl{baseUrl}/{resourceType}→/systems{baseUrl}/{resourceType}/{id}→/systems/abc123{baseUrl}/{resourceType}/{id}/{subPath}→/systems/abc123/subsystemsbuildQueryString()private buildQueryString(options?: QueryOptions): string— Parameter serialization with encodingundefined/nullvalues (skip them)id=sys1,sys2,sys3as comma-joined)?key=val&...when options presentPublic Property
public readonly availableResources: Set<string>— Exposes discovered resource types for user inspectionResource Validation Pattern
All public methods validate resource availability before building URLs (~2 lines per method):
Proof-of-Concept Public Methods (1-2 methods)
Implement 1-2 simple public methods as proof of concept using the private helpers:
getSystems(options?: SystemQueryOptions): string— Returns URL for listing systemsgetSystem(id: string, options?: QueryOptions): string— Returns URL for a single system by IDThese methods demonstrate the complete pattern: validate → build URL → return string. All 78 remaining methods (Phase 2, issues #5-#13) follow this exact pattern.
JSDoc Requirements
@seelink to CSAPI Part 1 specificationavailableResourcespropertysrc/ogc-api/edr/url_builder.tsTesting Requirements
src/ogc-api/csapi/url_builder.spec.ts(~100-150 lines)availableResourcesfrom collection linksavailableResources)EndpointErrorwhen resource type is unavailablegetSystems()returns correct URL with no optionsgetSystems({ limit: 10 })returns correct URL with query parametersgetSystem('abc123')returns correct URL with resource IDgetSystems({ bbox: [-180, -90, 180, 90] })returns correct URL with bbox parameterundefined/nullvaluessrc/ogc-api/edr/helpers.spec.tsandsrc/ogc-api/edr/model.spec.tsglobalThis.fetch = jest.fn()if HTTP mocking is needed (unlikely for this stub)Scope — What NOT to Touch
model.ts— that belongs to Phase 1.1 (issue Phase 1.1: Create Type System (csapi/model.ts) #1)helpers.ts— that belongs to Phase 1.2 (issue Phase 1.2: Create Helper Utilities (csapi/helpers.ts) #2)endpoint.ts— that belongs to Phase 1.4 (issue Phase 1.4: Integrate with OgcApiEndpoint #4)index.tsexports — that belongs to Phase 1.4 (issue Phase 1.4: Integrate with OgcApiEndpoint #4)formats/) — those belong to Phase 3 (issues Phase 3.1: GeoJSON Handler Extensions #14-Phase 3, Task 17: Format Index #30).system(id).datastream(id)) — those belong to Phase 2Acceptance Criteria
src/ogc-api/csapi/url_builder.tsexists with theCSAPIQueryBuilderclassconstructoracceptingOgcApiCollectionInfoparameterpublic readonly availableResources: Set<string>propertyextractAvailableResources(),buildResourceUrl(),buildQueryString()getSystems,getSystem) with resource validationmodel.ts(issue Phase 1.1: Create Type System (csapi/model.ts) #1) use types defined there (e.g.,SystemQueryOptions,QueryOptions)helpers.ts(issue Phase 1.2: Create Helper Utilities (csapi/helpers.ts) #2) use utilities defined there if needed (e.g., temporal encoding)src/ogc-api/csapi/url_builder.spec.tsexists with tests for constructor, resource validation, and public methodsnpm test)Dependencies
Blocked by: #1 — Phase 1.1: Create Type System (QueryBuilder imports types from
model.ts), #2 — Phase 1.2: Create Helper Utilities (QueryBuilder may use temporal encoding and validation utilities fromhelpers.ts)Blocks: #4 — Phase 1.4: Integrate with OgcApiEndpoint (integration imports
CSAPIQueryBuilder), #5-#13 — Phase 2: All resource method implementations build on this stubOperational Constraints
Key constraints for this task:
References
Read these documents before starting implementation. They are ordered by priority.
Primary References (must read)
docs/planning/csapi-implementation-guide.md§6: "Resource Validation Strategy"availableResources, and per-method validation withEndpointErrordocs/planning/csapi-implementation-guide.md§6: "Helper Methods"buildResourceUrl(),buildQueryString(),extractAvailableResources()with full code blocks showing signatures, implementation logic, and link-parsing patternsrc/ogc-api/edr/url_builder.tsexport default class, import style, and JSDoc formatsrc/ogc-api/edr/helpers.spec.tsdescribe/itstructure and assertion style for test filesUpstream Type/Import References (files this task imports from)
src/ogc-api/csapi/model.ts(created in issue #1)SystemQueryOptions,QueryOptions,CSAPIResourceTypesrc/ogc-api/model.tsOgcApiCollectionInfo(collection metadata type passed to constructor)src/ogc-api/csapi/helpers.ts(created in issue #2)buildQueryString()src/shared/models.tsEndpointError(for validation error messages)Research References (context, not required reading)
docs/research/upstream/querybuilder-pattern-analysis.mddocs/research/upstream/url-building-analysis.mdbuildResourceUrluses this specific signaturedocs/research/design/csapiquerybuilder/architecture-decision/results/DECISION-part1-structure.mddocs/research/design/csapiquerybuilder/architecture-decision/results/DECISION-part2-implementation.mddocs/planning/ROADMAP.mdPhase 1, Task 3Specification References (for
@seelinks in JSDoc)/systems,/deployments, etc.) for URL construction/datastreams,/observations, etc.)docs/research/standards/ogcapi-connectedsystems-1.bundled.oas31.yamldocs/research/standards/ogcapi-connectedsystems-2.bundled.oas31.yamlConvention Quick Reference
.jsextension for relative importsimport { OgcApiCollectionInfo } from '../model.js'import typefor interfaces/typesimport type { SystemQueryOptions } from './model.js'export default classfor QueryBuilderexport default class CSAPIQueryBuilder { ... }constructor(private collection_: OgcApiCollectionInfo)export interface QueryOptions { ... }globalThis.fetch = jest.fn()