Skip to content

Conversation

@jcpitre
Copy link
Collaborator

@jcpitre jcpitre commented Nov 25, 2025

Summary:
closes #1345
Added some license related fields to the feeds response, namely:

  • source_info->license_id
  • source_info->license_is_spdx
  • source_info->license_notes

Added the /v1/licenses endpoint (see examples below).

Still to be done:

  • Return the content_txt and content_html in a separate endpoint call. Suggestion:
    Add a include_license_content parameter to the /v1/licenses/{id} endpoint.

  • license_rules part of the response /v1/licenses contains the name of the rule. Maybe we want to use the label instead

  • license_rules could have its own endpoint?

AI summary

This pull request adds full support for managing and exposing license information for feeds in the API. It introduces new endpoints for license retrieval, updates feed models and test data to include license references, and ensures license relationships are efficiently loaded from the database. The changes also include new integration tests to verify license functionality.

Licenses API and Model Integration

  • Added new API endpoints and implementation for listing and retrieving licenses, including associated license rules, via LicensesApiImpl and OpenAPI generator files. [1] [2] [3] [4] [5]
  • Updated the database population script to insert licenses and license-rule associations from test data, ensuring feeds can reference licenses and rules. [1] [2]

Feed Model and Query Enhancements

  • Modified feed queries to efficiently join and load license relationships, preventing N+1 query issues and making license info available for model conversion. [1] [2] [3]
  • Updated BasicFeedImpl.from_orm to expose license fields (license_id, license_is_spdx, license_notes) in API responses. [1] [2]
  • Adjusted feed population logic to set license_id and license_notes from test data, handling missing/empty values gracefully.

Testing and Documentation

  • Added integration and unit tests for license endpoints and feed license fields, verifying correct API behavior and data relationships. [1] [2] [3]
  • Extended OpenAPI documentation and test data to cover license entities and their associations. [1] [2] [3] [4] [5]

Testing tips:

On top of the random queries listed below, added some integration tests.

Provide tips, procedures and sample files on how to test the feature.
Testers are invited to follow the tips AND to try anything they deem relevant outside the bounds of the testing tips.

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the unit tests with ./scripts/api-tests.sh to make sure you didn't break anything
  • Add or update any needed documentation to the repo
  • Format the title like "feat: [new feature short description]". Title must follow the Conventional Commit Specification(https://www.conventionalcommits.org/en/v1.0.0/).
  • Linked all relevant issues
  • Include screenshot(s) showing how this pull request works and fixes the issue(s)

@jcpitre jcpitre linked an issue Nov 25, 2025 that may be closed by this pull request
@jcpitre jcpitre marked this pull request as draft November 25, 2025 18:24
@jcpitre
Copy link
Collaborator Author

jcpitre commented Nov 25, 2025

Here is an example of the /v1/feeds/{id} call:

curl -X 'GET' 'http://localhost:8080/v1/feeds/mdb-1172' -H 'accept: application/json'
{
  "id": "mdb-1172",
  "data_type": "gtfs",
  "created_at": "2024-02-08T00:00:00Z",
  "external_ids": [
    {
      "external_id": "1172",
      "source": "mdb"
    }
  ],
  "provider": "Verkehrsverbund Mittelthüringen (VMT)",
  "feed_contact_email": "[email protected]",
  "source_info": {
    "producer_url": "https://www.vmt-thueringen.de/fileadmin/VMT_Redaktion/OPEN_DATA/VMT_GTFS.zip",
    "authentication_type": 0,
    "authentication_info_url": "",
    "api_key_parameter_name": "",
    "license_url": "https://creativecommons.org/licenses/by-nd/2.0/de/",
    "license_id": "CC-BY-ND-2.0",
    "license_is_spdx": true,
    "license_notes": "Detected locale/jurisdiction port 'de'. SPDX does not list ported CC licenses; using canonical ID."
  },
  "redirects": [],
  "status": "future",
  "official": true,
  "official_updated_at": "2025-01-22T20:19:42.509622",
  "feed_name": "",
  "note": "",
  "related_links": []
}

And for the /v1/licenses/{id} call (Updated after commit 1b21765)

curl -X 'GET' 'http://localhost:8080/v1/licenses/CC-BY-4.0' -H 'accept: application/json'
{
  "id": "CC-BY-4.0",
  "type": "standard",
  "is_spdx": true,
  "name": "Creative Commons Attribution 4.0 International",
  "url": "https://creativecommons.org/licenses/by/4.0/legalcode",
  "description": null,
  "created_at": "2025-11-19T21:57:02.519627",
  "updated_at": "2025-11-19T21:57:02.519685",
  "license_rules": [
    {
      "name": "commercial-use",
      "label": "Commercial use",
      "description": "This license allows the software or data to be used for commercial purposes.",
      "type": "permission"
    },
    {
      "name": "modifications",
      "label": "Modification",
      "description": "This license allows the software or content to be modified.",
      "type": "permission"
    },
    {
      "name": "distribution",
      "label": "Distribution",
      "description": "This license allows the software or content to be redistributed.",
      "type": "permission"
    },
    {
      "name": "private-use",
      "label": "Private use",
      "description": "This license allows private use and modification.",
      "type": "permission"
    },
    {
      "name": "patent-use",
      "label": "Patent use",
      "description": "This license includes an express grant of patent rights from contributors.",
      "type": "permission"
    },
    {
      "name": "include-copyright",
      "label": "Include copyright",
      "description": "A copy of the license and copyright notice must be included.",
      "type": "condition"
    },
    {
      "name": "document-changes",
      "label": "Document changes",
      "description": "Changes made must be clearly documented.",
      "type": "condition"
    },
    {
      "name": "trademark-use",
      "label": "Trademark use",
      "description": "The license does not grant rights to use trademarks.",
      "type": "limitation"
    },
    {
      "name": "liability",
      "label": "Liability disclaimer",
      "description": "The license includes a limitation of liability.",
      "type": "limitation"
    },
    {
      "name": "warranty",
      "label": "No warranty",
      "description": "The license explicitly states that no warranty is provided.",
      "type": "limitation"
    }
  ]
}

@jcpitre
Copy link
Collaborator Author

jcpitre commented Nov 25, 2025

Example of /v1/licenses (Updated after commit 1b21765)

curl -X 'GET' 'http://localhost:8080/v1/licenses?limit=2' -H 'accept: application/json'
[
  {
    "id": "0BSD",
    "type": "standard",
    "is_spdx": true,
    "name": "BSD Zero Clause License",
    "url": "https://opensource.org/licenses/0BSD",
    "description": null,
    "created_at": "2025-11-19T21:57:02.217375",
    "updated_at": "2025-11-19T21:57:02.217486"
  },
  {
    "id": "3D-Slicer-1.0",
    "type": "standard",
    "is_spdx": true,
    "name": "3D Slicer License v1.0",
    "url": "https://github.com/Slicer/Slicer/blob/main/License.txt",
    "description": null,
    "created_at": "2025-11-19T21:57:02.223643",
    "updated_at": "2025-11-19T21:57:02.223726"
  }
]
``1
Note that license_rules is not present when getting a list of licenses. 

@jcpitre jcpitre changed the title 1345 add license information to the api feat: add license information to the api Nov 25, 2025
@jcpitre jcpitre marked this pull request as ready for review November 25, 2025 20:00
@emmambd
Copy link
Collaborator

emmambd commented Nov 25, 2025

Wonder what purpose license_notes serves and why it's in the feeds endpoint but not the license ones?

@github-actions
Copy link

github-actions bot commented Nov 25, 2025

*Lighthouse ran on https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app/ * (Desktop)
⚡️ HTML Report Lighthouse report for the changes in this PR:

Performance Accessibility Best Practices SEO
🟠 78 🟢 100 🟢 100 🟢 100

*Lighthouse ran on https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app/feeds * (Desktop)
⚡️ HTML Report Lighthouse report for the changes in this PR:

Performance Accessibility Best Practices SEO
🟠 86 🟢 91 🟢 100 🟢 100

*Lighthouse ran on https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app/feeds/gtfs/mdb-2126 * (Desktop)
⚡️ HTML Report Lighthouse report for the changes in this PR:

Performance Accessibility Best Practices SEO
🟠 74 🔴 0 🟢 93 🟢 100

*Lighthouse ran on https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app/feeds/gtfs_rt/mdb-2585 * (Desktop)
⚡️ HTML Report Lighthouse report for the changes in this PR:

Performance Accessibility Best Practices SEO
🟢 91 🟠 87 🟢 100 🟢 100

*Lighthouse ran on https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app/gbfs/gbfs-flamingo_porirua * (Desktop)
⚡️ HTML Report Lighthouse report for the changes in this PR:

Performance Accessibility Best Practices SEO
🟢 100 🟢 100 🟢 100 🟢 100

@github-actions
Copy link

Preview Firebase Hosting URL: https://mobility-feeds-dev--pr-1482-e7wegjpi.web.app

@jcpitre
Copy link
Collaborator Author

jcpitre commented Nov 26, 2025

Wonder what purpose license_notes serves and why it's in the feeds endpoint but not the license ones?

@emmambd this just reflects the data in the database. I guess the license_notes are specific to the feed, maybe adding info on how the license was obtained, or how it applies to that feed?

@@ -0,0 +1,56 @@
name: Verify TypeScript GBFS Validator Types Generation
Copy link
Member

Choose a reason for hiding this comment

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

👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks to @Alessandro100

Comment on lines 24 to 34
lic_dict = {
"id": lic.id,
"type": lic.type,
"is_spdx": lic.is_spdx,
"name": lic.name,
"url": lic.url,
"description": lic.description,
"license_rules": [r.name for r in getattr(lic, "rules", [])],
"created_at": lic.created_at,
"updated_at": lic.updated_at,
}
Copy link
Member

Choose a reason for hiding this comment

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

Add this to the models folder, and it will avoid repetition

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added license_base_impl.py and license_with_rules_impl.py

Comment on lines 23 to 30
# Determine license_is_spdx from the related License ORM if available
license_is_spdx = None
try:
if getattr(feed, "license", None) is not None:
license_is_spdx = feed.license.is_spdx
except Exception:
# be conservative and keep None if anything goes wrong
license_is_spdx = None
Copy link
Member

Choose a reason for hiding this comment

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

We can skip the try/except clause here.

Suggested change
# Determine license_is_spdx from the related License ORM if available
license_is_spdx = None
try:
if getattr(feed, "license", None) is not None:
license_is_spdx = feed.license.is_spdx
except Exception:
# be conservative and keep None if anything goes wrong
license_is_spdx = None
# Determine license_is_spdx from the related License ORM if available
license_is_spdx = None
if getattr(feed, "license", None) is not None:
license_is_spdx = feed.license.is_spdx

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

type: boolean
example: true
license_notes:
description: TBD
Copy link
Member

Choose a reason for hiding this comment

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

This is a generic text notes field. We can document it with:

Suggested change
description: TBD
description: Notes associated with the feed license.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated

example: 0BSD
type:
type: string
description: TBD
Copy link
Member

Choose a reason for hiding this comment

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

Let's connect to go over all pending TBD documention

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed all TBD values and put something that seemed correct.

"name": lic.name,
"url": lic.url,
"description": lic.description,
"license_rules": [r.name for r in getattr(lic, "rules", [])],
Copy link
Member

Choose a reason for hiding this comment

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

[suggestion]: We can expose here the name and the label of each rule.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


return cls(
**base_license.model_dump(),
license_rules=rules or None,
Copy link
Member

Choose a reason for hiding this comment

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

[suggestion]: return an empty array instead of none, it's safer to parse on the consumer side.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure about this one. The function is not supposed to return a list.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry for the confussion, I'm talking about the license_rules

@jcpitre jcpitre requested a review from davidgamez December 2, 2025 02:58
Copy link
Member

@davidgamez davidgamez left a comment

Choose a reason for hiding this comment

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

LGTM!

@davidgamez davidgamez merged commit 57525b1 into main Dec 2, 2025
7 checks passed
@davidgamez davidgamez deleted the 1345-add-license-information-to-the-api branch December 2, 2025 14:05
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.

Add license information to the API

6 participants