Skip to content

8191: Data adapter - #3

Merged
rimi-itk merged 54 commits into
developfrom
feature/8191-data-adapter
Sep 9, 2026
Merged

8191: Data adapter#3
rimi-itk merged 54 commits into
developfrom
feature/8191-data-adapter

Conversation

@jeppekroghitk

@jeppekroghitk jeppekroghitk commented Sep 1, 2026

Copy link
Copy Markdown

Link to ticket

https://leantime.itkdev.dk/#/tickets/showTicket/8191

Description

  • Added ImportCommand as the console front for running imports from the terminal
  • Added DataSourceImporter for converting a source and upserting it, with typed failures per outcome
  • Added SourceInterface for registering new data sources
  • Added DataSourceReader for fetching and decoding feeds
  • Added HandicapParking source for Aarhus disabled parking bays via MTM SpatialMaps
  • Added Wgs84Transformer for reprojecting coordinates to WGS84
  • Added NgsiEntity for normalized NGSI-LD output
  • Added NgsiLdBroker for idempotent batch upserts
  • Added source manifest (config/sources.yaml): one record per data set
  • Added Source\Manifest\Catalog, Schema and Validator for reading the manifest and failing the container build on an incomplete record
  • Added DataController for proxying broker queries back out, and a map page for reading them
  • Added Taskfile commands for importing and reading back entities
  • Added ADRs for the decisions behind the adapter
  • Added tests for the transformer, reader, mapping, importer, console command and source manifest
  • Moved the per-data-set configuration (feed URL, CRS, model) out of .env into the manifest

Screenshot of the result

N/A

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

@jeppekroghitk
jeppekroghitk changed the base branch from main to develop September 1, 2026 11:26

@rimi-itk rimi-itk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very good. Some refactoring can make it even gooder. In general we should be careful not to add too much login in commands and create services that can handle the job.

- **Containers are mandatory.** The template's PHP runs ahead of developer
hosts, so dependency management, console commands and tests cannot run
natively. Most likely source of first-run confusion.
- A web server, database and mail catcher are provisioned and never used.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove any unused services.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Removed this statement.

input declares its own CRS; reprojection happens at the boundary between reading
an input and building an entity, and nowhere else.

- **The specification is unambiguous.** RFC 7946 §4: "The coordinate reference

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is it unambiguous?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need ADRs we should write them ourselves. Much of the text makes no sense to me.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that they could be even shorter and more concise. I think that some of the considerations behind the decisions makes a stronger argument towards the decided architecture. People in the future may use this project as inspiration for working with brokers, so i think that the proper documentation earns its place.

Comment thread README.md
Comment on lines +29 to 34
``` shell
task import # list the available sources
task import -- mtm_spatialmaps-handicap-parking # import one
task import -- mtm_spatialmaps-handicap-parking --dry-run --limit 5 # print the payload instead
task broker:entities -- OnStreetParking 10 # read back what landed
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thinks we should tell how to run these commands with docker compose rather than with a task. We (currently) mostly use task for local development and for productions one should use docker compose (and we'll use itkdev-docker-compose-server, but that's our choice and caused by our hosting setup).

Comment thread README.md Outdated
task import # list the available sources
task import -- mtm_spatialmaps-handicap-parking # import one
task import -- mtm_spatialmaps-handicap-parking --dry-run --limit 5 # print the payload instead
task broker:entities -- OnStreetParking 10 # read back what landed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This task makes sense only during local development, right? I would remove it and add a curl example on how to talk to the broker (and then refer to reader to the NGSI-LD API documentation elsewhere).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed the broker:entities task for now, as it was kinda hacky.

I think the import tasks are pretty nifty when working with new datasets, so i want to keep them for now..

Comment thread src/Source/SourceCatalog.php Outdated
Comment on lines +14 to +15
* Parsed on first use rather than during container warm-up, so a malformed
* entry fails the import that needs it instead of every cache clear.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle this as any other Symfony config, i.e. read and parse and validate it during container warm-up and define the sources a real config (cf. https://symfony.com/doc/current/components/config.html)

Comment thread src/Source/FeedReader.php Outdated
Comment thread src/Source/FeedReader.php Outdated
Comment on lines +40 to +42
// A JSON document may legally be a scalar. Every feed we consume is a
// list or an object, and a scalar here means the location is wrong
// rather than that the feed is empty.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle HTTP errors in a better way (than just throwing \RuntimeExceptions) and maybe even report them back to the caller.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\HttpClient\MockHttpClient;

class ImportCommandTest extends TestCase

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be refactored to be a DataSourceImporterTest, i.e. something the tests an import service rather than a command container too much logic.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

jeppekroghitk and others added 25 commits September 7, 2026 12:55
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Co-authored-by: Mikkel Ricky <rimi@aarhus.dk>
Replaces the hand-written field checks with a config tree, and reads the
whole manifest during container warm-up, so a malformed entry fails the
build instead of the one import that happens to select it.
Catalog, Descriptor, Schema and Validator now sit together, which drops the repeated Source prefix. The cache warmer became Validator: it caches nothing, and exists so a malformed record fails the build.

Schema no longer implements ConfigurationInterface — that is a bundle extension contract, not a description of a committed data file — so the catalogue hands the built tree to Processor::process() instead.
/**
* Identifier used to select this source on the command line.
*/
public function key(): string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if using https://symfony.com/doc/current/service_container/subscribers_locators.html#injecting-a-service-locator would be better suited for this, i.e. something like

        arguments:
          - !service_locator
              mtm_spatialmaps-handicap-parking: '@App\Source\MtmSpatialMaps\HandicapParking'
              

where we define the key in the config rather than in the class.

@rimi-itk
rimi-itk merged commit 58872f6 into develop Sep 9, 2026
8 checks passed
@rimi-itk
rimi-itk deleted the feature/8191-data-adapter branch September 9, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants