From ab57931e0e97cc2788e74c36937b786978ea362a Mon Sep 17 00:00:00 2001 From: ejacqu22 <2057151+etiennej70@users.noreply.github.com> Date: Thu, 2 Apr 2026 18:04:33 +0200 Subject: [PATCH 1/2] chore(init): init repository --- .github/dependabot.yml | 60 + .github/pull_request_template.md | 86 ++ .github/scripts/check_navigation.sh | 71 ++ .github/scripts/extract_specs.sh | 74 ++ .github/scripts/summarize_owasp.sh | 37 + .github/tests/test_check_navigation.bats | 278 +++++ .github/tests/test_summarize_owasp.bats | 78 ++ .github/workflows/basic_code_checks.yml | 358 ++++++ .github/workflows/copilot-setup-steps.yml | 45 + .github/workflows/deploy_docs.yml | 83 ++ .github/workflows/lint_pull_request_title.yml | 52 + .gitignore | 82 ++ .markdownlint.yaml | 226 ++++ .pre-commit-config.yaml | 48 + .vale.ini | 15 + .../styles/config/vocabularies/IDP/accept.txt | 39 + .../styles/config/vocabularies/IDP/reject.txt | 0 .yamllint.yaml | 14 + CODE_OF_CONDUCT.md | 90 ++ CONTRIBUTING.md | 10 + Dockerfile | 15 + GOVERNANCE.md | 48 + NOTICE | 13 + OWNERS.md | 29 + README.md | 33 + SECURITY.md | 68 ++ docker-compose.yml | 21 + docs/.gitignore | 24 + docs/.python-version | 1 + docs/pyproject.toml | 14 + docs/src/api/index.md | 82 ++ docs/src/concepts/entities.md | 171 +++ docs/src/concepts/entity-templates.md | 235 ++++ docs/src/concepts/index.md | 126 ++ docs/src/concepts/properties.md | 400 +++++++ docs/src/concepts/relations.md | 223 ++++ .../contributing/adrs/0001-doc-site-engine.md | 95 ++ docs/src/contributing/adrs/_template.md | 77 ++ docs/src/contributing/adrs/index.md | 9 + docs/src/contributing/ci-workflow.md | 69 ++ docs/src/contributing/code/best-practices.md | 24 + .../src/contributing/code/code-conventions.md | 225 ++++ .../code/domain-infrastructure.md | 42 + .../code/domain-model-validations.md | 27 + .../contributing/code/exception-handling.md | 33 + docs/src/contributing/code/index.md | 75 ++ docs/src/contributing/development-setup.md | 162 +++ docs/src/contributing/documentation.md | 360 ++++++ docs/src/contributing/index.md | 93 ++ docs/src/contributing/pull-requests.md | 228 ++++ docs/src/contributing/testing.md | 24 + docs/src/deployment/configuration.md | 277 +++++ docs/src/deployment/docker.md | 332 ++++++ docs/src/deployment/index.md | 57 + docs/src/deployment/kubernetes.md | 400 +++++++ docs/src/deployment/observability.md | 338 ++++++ docs/src/features/data-integration.md | 288 +++++ docs/src/features/index.md | 60 + docs/src/features/scorecards.md | 392 +++++++ docs/src/features/self-service-actions.md | 594 ++++++++++ docs/src/getting-started/configuration.md | 267 +++++ docs/src/getting-started/index.md | 120 ++ docs/src/getting-started/installation.md | 44 + docs/src/getting-started/quickstart.md | 266 +++++ docs/src/index.md | 167 +++ docs/src/javascripts/mathjax.js | 18 + docs/src/javascripts/mermaid-init.js | 52 + docs/src/static/swagger.yaml | 579 ++++++++++ docs/uv.lock | 176 +++ docs/zensical.toml | 192 ++++ pom.xml | 381 +++++++ .../idp_core/IdpCoreApplication.java | 13 + .../configuration/JacksonConfiguration.java | 24 + .../domain/constant/ValidationsMessages.java | 24 + .../exception/EntityNotFoundException.java | 19 + .../EntityTemplateAlreadyExistsException.java | 40 + .../EntityTemplateNotFoundException.java | 96 ++ .../idp_core/domain/model/entity/Entity.java | 87 ++ .../domain/model/entity/EntitySummary.java | 19 + .../domain/model/entity/Property.java | 37 + .../domain/model/entity/Relation.java | 66 ++ .../model/entity/RelationAsTargetSummary.java | 14 + .../model/entity_template/EntityTemplate.java | 144 +++ .../entity_template/PropertyDefinition.java | 150 +++ .../model/entity_template/PropertyRules.java | 152 +++ .../entity_template/RelationDefinition.java | 129 +++ .../domain/model/enums/PropertyFormat.java | 6 + .../domain/model/enums/PropertyType.java | 7 + .../domain/repository/EntityRepository.java | 61 + .../repository/EntityTemplateRepository.java | 109 ++ .../domain/repository/RelationRepository.java | 37 + .../domain/service/EntityService.java | 88 ++ .../domain/service/EntityTemplateService.java | 279 +++++ .../domain/service/RelationService.java | 34 + .../configuration/SecurityConfiguration.java | 27 + .../configuration/SwaggerConfiguration.java | 94 ++ .../api/configuration/SwaggerDescription.java | 126 ++ .../api/configuration/WebConfiguration.java | 16 + .../api/controller/EntityController.java | 144 +++ .../controller/EntityTemplateController.java | 248 ++++ .../api/dto/in/EntityDtoIn.java | 34 + .../api/dto/in/EntityTemplateDtoIn.java | 48 + .../api/dto/in/PropertyDefinitionDtoIn.java | 52 + .../api/dto/in/PropertyRulesDtoIn.java | 43 + .../api/dto/in/RelationDefinitionDtoIn.java | 44 + .../api/dto/out/entity/EntityDtoOut.java | 24 + .../api/dto/out/entity/EntitySummaryDto.java | 17 + .../entitytemplate/EntityTemplateDtoOut.java | 34 + .../PropertyDefinitionDtoOut.java | 36 + .../entitytemplate/PropertyRulesDtoOut.java | 60 + .../RelationDefinitionDtoOut.java | 33 + .../api/handler/ApiExceptionHandler.java | 239 ++++ .../api/mapper/entity/EntityDtoInMapper.java | 55 + .../api/mapper/entity/EntityDtoOutMapper.java | 349 ++++++ .../entitytemplate/EntityTemplateMapper.java | 262 +++++ src/main/resources/application-dev.properties | 25 + src/main/resources/application-local.yml | 39 + src/main/resources/application.yml | 77 ++ .../db/local/R__1_Insert_sample_data.sql | 152 +++ .../local/R__2_Insert_entities_test_data.sql | 18 + .../V1_1__Create_property_rules_table.sql | 25 + ...V1_2__Create_property_definition_table.sql | 26 + ...V1_3__Create_relation_definition_table.sql | 23 + .../V1_4__Create_entity_template_table.sql | 19 + .../V1_5__Create_junction_tables.sql | 30 + .../migration/V2_1__create_entity_table.sql | 9 + .../migration/V2_2__create_property_table.sql | 7 + .../V2_3__create_entity_properties_table.sql | 11 + .../migration/V2_4__create_relation_table.sql | 6 + ..._create_relation_target_entities_table.sql | 7 + .../V2_6__create_entity_relations.sql | 10 + .../idp_core/AbstractIntegrationTest.java | 211 ++++ .../idp_core/TestSecurityConfiguration.java | 21 + .../api/controller/EntityControllerTest.java | 184 +++ .../EntityTemplateControllerTest.java | 1009 +++++++++++++++++ .../api/controller/HealthControllerTest.java | 20 + .../api/handler/ApiExceptionHandlerTest.java | 437 +++++++ .../api/mapper/EntityTemplateMapperTest.java | 615 ++++++++++ src/test/resources/application-test.yml | 40 + .../db/test/R__1_Insert_test_data.sql | 280 +++++ .../test/R__2_Insert_entities_test_data.sql | 18 + ...mplateWithoutRelationsDefinitions_201.json | 22 + .../v1/postEntityTemplate_201.json | 22 + ...ntityTemplate_400_bad_property_format.json | 22 + ...tEntityTemplate_400_bad_property_type.json | 22 + ...stEntityTemplate_400_identifier_blank.json | 22 + ...EntityTemplate_400_identifier_missing.json | 21 + ...stEntityTemplate_400_properties_empty.json | 6 + ...mplate_400_property_description_blank.json | 22 + ...late_400_property_description_missing.json | 21 + ...ntityTemplate_400_property_name_blank.json | 22 + ...ityTemplate_400_property_name_missing.json | 21 + ...ityTemplate_400_property_type_missing.json | 21 + ...emplate_409_identifier_already_exists.json | 22 + .../v1/putEntityTemplate_200.json | 26 + ...mplate_400_propertyDescriptionIsBlank.json | 26 + ...late_400_propertyDescriptionIsMissing.json | 25 + ...ntityTemplate_400_propertyNameIsBlank.json | 26 + ...ityTemplate_400_propertyNameIsMissing.json | 25 + ...ityTemplate_400_propertyTypeIsMissing.json | 25 + ...late_400_withoutPropertiesDefinitions.json | 6 + ...plate_409_withIdentifierAlreadyExists.json | 26 + .../v1/putEntityTemplate_updateRules_200.json | 17 + .../json/entity/v1/postEntity_201.json | 9 + src/test/resources/junit-platform.properties | 1 + 165 files changed, 17209 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100755 .github/scripts/check_navigation.sh create mode 100755 .github/scripts/extract_specs.sh create mode 100755 .github/scripts/summarize_owasp.sh create mode 100755 .github/tests/test_check_navigation.bats create mode 100644 .github/tests/test_summarize_owasp.bats create mode 100644 .github/workflows/basic_code_checks.yml create mode 100644 .github/workflows/copilot-setup-steps.yml create mode 100644 .github/workflows/deploy_docs.yml create mode 100644 .github/workflows/lint_pull_request_title.yml create mode 100644 .gitignore create mode 100644 .markdownlint.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 .vale.ini create mode 100644 .vale/styles/config/vocabularies/IDP/accept.txt create mode 100644 .vale/styles/config/vocabularies/IDP/reject.txt create mode 100644 .yamllint.yaml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Dockerfile create mode 100644 GOVERNANCE.md create mode 100644 NOTICE create mode 100644 OWNERS.md create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 docker-compose.yml create mode 100644 docs/.gitignore create mode 100644 docs/.python-version create mode 100644 docs/pyproject.toml create mode 100644 docs/src/api/index.md create mode 100644 docs/src/concepts/entities.md create mode 100644 docs/src/concepts/entity-templates.md create mode 100644 docs/src/concepts/index.md create mode 100644 docs/src/concepts/properties.md create mode 100644 docs/src/concepts/relations.md create mode 100644 docs/src/contributing/adrs/0001-doc-site-engine.md create mode 100644 docs/src/contributing/adrs/_template.md create mode 100644 docs/src/contributing/adrs/index.md create mode 100644 docs/src/contributing/ci-workflow.md create mode 100644 docs/src/contributing/code/best-practices.md create mode 100644 docs/src/contributing/code/code-conventions.md create mode 100644 docs/src/contributing/code/domain-infrastructure.md create mode 100644 docs/src/contributing/code/domain-model-validations.md create mode 100644 docs/src/contributing/code/exception-handling.md create mode 100644 docs/src/contributing/code/index.md create mode 100644 docs/src/contributing/development-setup.md create mode 100644 docs/src/contributing/documentation.md create mode 100644 docs/src/contributing/index.md create mode 100644 docs/src/contributing/pull-requests.md create mode 100644 docs/src/contributing/testing.md create mode 100644 docs/src/deployment/configuration.md create mode 100644 docs/src/deployment/docker.md create mode 100644 docs/src/deployment/index.md create mode 100644 docs/src/deployment/kubernetes.md create mode 100644 docs/src/deployment/observability.md create mode 100644 docs/src/features/data-integration.md create mode 100644 docs/src/features/index.md create mode 100644 docs/src/features/scorecards.md create mode 100644 docs/src/features/self-service-actions.md create mode 100644 docs/src/getting-started/configuration.md create mode 100644 docs/src/getting-started/index.md create mode 100644 docs/src/getting-started/installation.md create mode 100644 docs/src/getting-started/quickstart.md create mode 100644 docs/src/index.md create mode 100644 docs/src/javascripts/mathjax.js create mode 100644 docs/src/javascripts/mermaid-init.js create mode 100644 docs/src/static/swagger.yaml create mode 100644 docs/uv.lock create mode 100644 docs/zensical.toml create mode 100644 pom.xml create mode 100644 src/main/java/com/decathlon/idp_core/IdpCoreApplication.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/configuration/JacksonConfiguration.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/constant/ValidationsMessages.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/exception/EntityNotFoundException.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/exception/EntityTemplateAlreadyExistsException.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/exception/EntityTemplateNotFoundException.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity/Entity.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity/EntitySummary.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity/Property.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity/Relation.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity/RelationAsTargetSummary.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity_template/EntityTemplate.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity_template/PropertyDefinition.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity_template/PropertyRules.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/entity_template/RelationDefinition.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/enums/PropertyFormat.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/model/enums/PropertyType.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/repository/EntityRepository.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/repository/EntityTemplateRepository.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/repository/RelationRepository.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/service/EntityService.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/service/EntityTemplateService.java create mode 100644 src/main/java/com/decathlon/idp_core/domain/service/RelationService.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/configuration/SecurityConfiguration.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/configuration/SwaggerConfiguration.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/configuration/SwaggerDescription.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/configuration/WebConfiguration.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/controller/EntityController.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/controller/EntityTemplateController.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/in/EntityDtoIn.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/in/EntityTemplateDtoIn.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/in/PropertyDefinitionDtoIn.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/in/PropertyRulesDtoIn.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/in/RelationDefinitionDtoIn.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entity/EntityDtoOut.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entity/EntitySummaryDto.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entitytemplate/EntityTemplateDtoOut.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entitytemplate/PropertyDefinitionDtoOut.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entitytemplate/PropertyRulesDtoOut.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/dto/out/entitytemplate/RelationDefinitionDtoOut.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/handler/ApiExceptionHandler.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/mapper/entity/EntityDtoInMapper.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/mapper/entity/EntityDtoOutMapper.java create mode 100644 src/main/java/com/decathlon/idp_core/infrastructure/api/mapper/entitytemplate/EntityTemplateMapper.java create mode 100644 src/main/resources/application-dev.properties create mode 100644 src/main/resources/application-local.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/db/local/R__1_Insert_sample_data.sql create mode 100644 src/main/resources/db/local/R__2_Insert_entities_test_data.sql create mode 100644 src/main/resources/db/migration/V1_1__Create_property_rules_table.sql create mode 100644 src/main/resources/db/migration/V1_2__Create_property_definition_table.sql create mode 100644 src/main/resources/db/migration/V1_3__Create_relation_definition_table.sql create mode 100644 src/main/resources/db/migration/V1_4__Create_entity_template_table.sql create mode 100644 src/main/resources/db/migration/V1_5__Create_junction_tables.sql create mode 100644 src/main/resources/db/migration/V2_1__create_entity_table.sql create mode 100644 src/main/resources/db/migration/V2_2__create_property_table.sql create mode 100644 src/main/resources/db/migration/V2_3__create_entity_properties_table.sql create mode 100644 src/main/resources/db/migration/V2_4__create_relation_table.sql create mode 100644 src/main/resources/db/migration/V2_5__create_relation_target_entities_table.sql create mode 100644 src/main/resources/db/migration/V2_6__create_entity_relations.sql create mode 100644 src/test/java/com/decathlon/idp_core/AbstractIntegrationTest.java create mode 100644 src/test/java/com/decathlon/idp_core/TestSecurityConfiguration.java create mode 100644 src/test/java/com/decathlon/idp_core/infrastructure/api/controller/EntityControllerTest.java create mode 100644 src/test/java/com/decathlon/idp_core/infrastructure/api/controller/EntityTemplateControllerTest.java create mode 100644 src/test/java/com/decathlon/idp_core/infrastructure/api/controller/HealthControllerTest.java create mode 100644 src/test/java/com/decathlon/idp_core/infrastructure/api/handler/ApiExceptionHandlerTest.java create mode 100644 src/test/java/com/decathlon/idp_core/infrastructure/api/mapper/EntityTemplateMapperTest.java create mode 100644 src/test/resources/application-test.yml create mode 100644 src/test/resources/db/test/R__1_Insert_test_data.sql create mode 100644 src/test/resources/db/test/R__2_Insert_entities_test_data.sql create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplateWithoutRelationsDefinitions_201.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_201.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_bad_property_format.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_bad_property_type.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_identifier_blank.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_identifier_missing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_properties_empty.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_property_description_blank.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_property_description_missing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_property_name_blank.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_property_name_missing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_400_property_type_missing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/postEntityTemplate_409_identifier_already_exists.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_200.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_propertyDescriptionIsBlank.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_propertyDescriptionIsMissing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_propertyNameIsBlank.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_propertyNameIsMissing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_propertyTypeIsMissing.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_400_withoutPropertiesDefinitions.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_409_withIdentifierAlreadyExists.json create mode 100644 src/test/resources/integration_test/json/entity-template/v1/putEntityTemplate_updateRules_200.json create mode 100644 src/test/resources/integration_test/json/entity/v1/postEntity_201.json create mode 100644 src/test/resources/junit-platform.properties diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ea541b4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,60 @@ +--- +version: 2 +updates: + # Maven dependencies + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "05:00" + open-pull-requests-limit: 10 + groups: + # Group all patch and minor updates together, individual PRs for major updates + all-minor-and-patch-updates: + patterns: + - "*" + update-types: + - "patch" + - "minor" + pull-request-branch-name: + separator: "-" + + # Docker dependencies + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "daily" + time: "05:00" + open-pull-requests-limit: 5 + pull-request-branch-name: + separator: "-" + + # GitHub Actions dependencies + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "05:00" + open-pull-requests-limit: 5 + groups: + # Group all minor and patch updates together + all-minor-and-patch-updates: + patterns: + - "*" + update-types: + - "minor" + - "patch" + pull-request-branch-name: + separator: "-" + + - package-ecosystem: "uv" + directory: "/docs" + schedule: + interval: "monthly" + day: "monday" + time: "05:00" + open-pull-requests-limit: 5 + pull-request-branch-name: + separator: "-" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..bc7c4fc --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,86 @@ +# PR Description + + + +## What this PR Provides + +- Describe in short sentences the goal of the PR. Use lists. +- If the PR is linked to an ADR, please provide the link. + +## Fixes + + + +## Review + + + +The reviewer **must** double-check these points: + +- [ ] The reviewer has tested the feature +- [ ] The reviewer has reviewed the implementation of the feature +- [ ] The documentation has been updated +- [ ] The feature implementation respects the Technical Doc / ADR previously produced +- [ ] The Pull Request title has a `!` after the type/scope to identify the breaking + change in the release note and ensure we will release a major version. + +## How to test + + + +Please **refer** (copy/paste) the test section **from the User Story**. This should include + +- The initial state: what should be the status of the system before testing + (for example, ensure the data xxx exists in idp-back to be able to test the feature) +- What and how to test: steps to perform to test the feature + (for example, go to page xxx, fill the xxx field and click the 'send' button) +- Expected results: what should be observed for success or failure + (for example, there is a link in the database between component X and component Y. + You can retrieve the information with a `GET` request to the API) + +## Breaking changes (if any) + + + +- Data loss / modification +- API JSON schema modification (existing resource / behavior) +- Behavior modification of a component +- Others +- N/A + +### Context of the Breaking Change + + + +For example: we redefined the component types list in the DPAC referential + +### Result of the Breaking Change + + + +For example: your component of type xxx will migrate to the type yyy diff --git a/.github/scripts/check_navigation.sh b/.github/scripts/check_navigation.sh new file mode 100755 index 0000000..0af0490 --- /dev/null +++ b/.github/scripts/check_navigation.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# Check for 'none' placeholders in Zensical navigation + +set -e + +SITE_DIR="${1:-docs/site}" +SRC_DIR="${2:-docs/src}" +EXIT_CODE=0 +declare -A broken_refs + +echo "🔍 Checking for broken navigation links in ${SITE_DIR}..." + +# Find files with 'none' placeholders in navigation +while IFS= read -r file; do + if [ -n "$file" ]; then + # Get line numbers where 'none' appears in md-ellipsis context + line_nums=$(grep -n "^\s*none\s*$" "$file" | cut -d: -f1 2>/dev/null || true) + + for line_num in $line_nums; do + # Check if this 'none' is within md-ellipsis span (check nearby lines) + # Ensure we don't use negative line numbers + start_line=$((line_num > 3 ? line_num - 3 : 1)) + end_line=$((line_num + 3)) + + if sed -n "${start_line},${end_line}p" "$file" | grep -q "md-ellipsis"; then + # Extract the href to find the source markdown file + # Look up to 10 lines before, but not before line 1 + href_start=$((line_num > 10 ? line_num - 10 : 1)) + href=$(sed -n "${href_start},${line_num}p" "$file" | grep -oP 'href="\K[^"]+' | tail -1 2>/dev/null || true) + + if [[ $href == *.md ]]; then + # Store the reference + broken_refs["$href"]=1 + fi + + EXIT_CODE=1 + fi + done + fi +done < <(find "$SITE_DIR" -name "*.html" -type f) + +if [ $EXIT_CODE -eq 0 ]; then + echo "✅ No broken navigation links found" +else + echo "" + echo "📝 Markdown files referenced in broken navigation links:" + echo "" + + # Show unique markdown file references + for ref in $(printf '%s\n' "${!broken_refs[@]}" | sort -u); do + # Extract just the filename for clarity + filename=$(basename "$ref") + + # Try to find it in the source directory + found_files=$(find "$SRC_DIR" -name "$filename" 2>/dev/null || true) + + if [ -n "$found_files" ]; then + echo "$found_files" | while read -r found; do + echo " ❌ $found" + done + else + echo " ⚠️ $filename (referenced as: $ref, not found in $SRC_DIR)" + fi + done | sort -u + + echo "" + echo "💡 Fix: Add 'title: Your Title' in YAML frontmatter" + echo " (at the top of the file between --- markers)" +fi + +exit $EXIT_CODE diff --git a/.github/scripts/extract_specs.sh b/.github/scripts/extract_specs.sh new file mode 100755 index 0000000..5ad8dea --- /dev/null +++ b/.github/scripts/extract_specs.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -euo pipefail + +# Define environment variables (passed by GitHub Actions) +# These default to the values defined in the workflow's 'env' block +API_PORT=${API_PORT:-8080} +SPEC_URL_PATH=${SPEC_URL_PATH:-/v3/api-docs/internal} +MAIN_SWAGGER="$PWD/docs/src/static/swagger.yaml" + +# Save the current branch name (the PR branch) +PR_BRANCH=$(git rev-parse --abbrev-ref HEAD) +# Fetch and checkout the main branch to get the reference +git fetch origin main +git checkout origin/main +# 1. Check if the static swagger file exists on the 'main' branch +if [ -f "$MAIN_SWAGGER" ]; then + # If it exists, copy it as the reference spec + cp "$MAIN_SWAGGER" "specs/static-swagger-main.yaml" +else + printf -- "Static swagger missing in main, extracting from main JAR\n" + # Build the project on the 'main' branch to get the JAR + mvn clean package -B + + JAR_PATH=$(find target -maxdepth 1 -name "*.jar" | head -n 1) + if [ -z "$JAR_PATH" ]; then + printf -- "::error:: No JAR found in target/ folder.\n" + exit 1 + fi + + # Start the application in the background (&) + java -jar "$JAR_PATH" --server.port="$API_PORT" & + MAIN_PID=$! + + # --- START OF RELIABLE POLLING LOGIC --- + MAX_ATTEMPTS=20 # Maximum number of attempts + SLEEP_TIME=3 # Wait time (in seconds) between attempts + ATTEMPT=0 # Initial attempt counter + API_URL="http://localhost:$API_PORT$SPEC_URL_PATH" + + printf -- "Waiting for API endpoint (%s) to be reachable...\n" "$API_URL" + + while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do + # Use curl with --fail and --silent to check for a successful connection (HTTP 2xx response) + if curl --fail --silent -o /dev/null "$API_URL"; then + printf -- "API is up after %d seconds.\n" "$((ATTEMPT * SLEEP_TIME))" + break # Exit the loop because the API is ready + fi + printf -- "Attempt %d/%d failed. Waiting %d seconds...\n" "$((ATTEMPT + 1))" "$MAX_ATTEMPTS" "$SLEEP_TIME" + sleep "$SLEEP_TIME" + ATTEMPT=$((ATTEMPT + 1)) + done + # Check if the maximum number of attempts was reached (timeout) + if [ "$ATTEMPT" -eq "$MAX_ATTEMPTS" ]; then + printf -- "::error:: Application failed to start and expose Swagger endpoint after %d attempts.\n" "$MAX_ATTEMPTS" + printf -- "Swagger endpoint unreachable. Dumping logs:\n" + pgrep -a java || printf -- "No Java process found\n" + kill "$MAIN_PID" + exit 1 # Fail the job + fi + # --- END OF RELIABLE POLLING LOGIC --- + + printf -- "Downloading specification from running application...\n" + if ! curl -f "$API_URL" > "specs/static-swagger-main.yaml"; then + printf -- "::error:: Failed to download OpenAPI specification after successful startup check.\n" + kill "$MAIN_PID" + exit 1 + fi + + kill "$MAIN_PID" + printf -- "Reference swagger successfully generated and saved as static-swagger-main.yaml\n" +fi + +# Return to the original PR branch +git checkout "$PR_BRANCH" diff --git a/.github/scripts/summarize_owasp.sh b/.github/scripts/summarize_owasp.sh new file mode 100755 index 0000000..561bc04 --- /dev/null +++ b/.github/scripts/summarize_owasp.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +SUMMARY_FILE="${GITHUB_STEP_SUMMARY:-/dev/stdout}" + +echo "### OWASP Dependency Check Summary" >> "$SUMMARY_FILE" + +SARIF_REPORT="" +if [ -f "$PWD/target/security-reports/dependency-check-report.sarif" ]; then + SARIF_REPORT="$PWD/target/security-reports/dependency-check-report.sarif" +elif [ -f "$PWD/target/dependency-check-report.sarif" ]; then + SARIF_REPORT="$PWD/target/dependency-check-report.sarif" +fi + +if [ -n "$SARIF_REPORT" ]; then + echo "| Vulnerability | CVSS | Package |" >> "$SUMMARY_FILE" + echo "|:--|:--:|:--|" >> "$SUMMARY_FILE" + + if command -v jq >/dev/null 2>&1; then + VULN_COUNT=$(jq '.runs[].results | length' "$SARIF_REPORT" | awk '{s+=$1} END {print s}') + if [ "$VULN_COUNT" -gt 0 ]; then + jq -r '.runs[].results[] | [.ruleId, (.properties.cvssScore // "N/A"), (.message.text | split("\n")[0])] | @tsv' "$SARIF_REPORT" | + while IFS=$'\t' read -r ruleId cvss message; do + printf "| %s | %s | %s |\n" "$ruleId" "$cvss" "$message" >> "$SUMMARY_FILE" + done + echo "| **Total** | **$VULN_COUNT** | |" >> "$SUMMARY_FILE" + echo "::error:: OWASP Dependency Check found $VULN_COUNT vulnerabilities" + exit 1 + else + echo "| OK | 0 | No vulnerabilities found |" >> "$SUMMARY_FILE" + fi + else + echo "::warning:: jq not found — unable to parse SARIF details" + fi +else + echo "::warning:: OWASP report missing" +fi diff --git a/.github/tests/test_check_navigation.bats b/.github/tests/test_check_navigation.bats new file mode 100755 index 0000000..8de0056 --- /dev/null +++ b/.github/tests/test_check_navigation.bats @@ -0,0 +1,278 @@ +#!/usr/bin/env bats + +# Test for check_navigation.sh script + +setup() { + # Create temporary test directory + TEST_DIR="$(mktemp -d)" + SITE_DIR="$TEST_DIR/site" + SRC_DIR="$TEST_DIR/src" + mkdir -p "$SITE_DIR" "$SRC_DIR" + + SCRIPT_PATH="${BATS_TEST_DIRNAME}/../scripts/check_navigation.sh" +} + +teardown() { + # Clean up temporary directory + rm -rf "$TEST_DIR" +} + +@test "exits with 0 when no HTML files exist" { + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 0 ] + [[ "$output" =~ "✅ No broken navigation links found" ]] +} + +@test "exits with 0 when HTML files have no 'none' placeholders" { + cat > "$SITE_DIR/test.html" < + + + Valid Title + + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 0 ] + [[ "$output" =~ "✅ No broken navigation links found" ]] +} + +@test "detects 'none' placeholder in navigation" { + cat > "$SITE_DIR/test.html" < + + + + none + + + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "📝 Markdown files referenced in broken navigation links" ]] +} + +@test "shows markdown filename in error output" { + cat > "$SITE_DIR/page.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "missing-title.md" ]] +} + +@test "handles multiple broken links in same file" { + cat > "$SITE_DIR/multi.html" < + +
  • + + + none + + +
  • +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "first.md" ]] + [[ "$output" =~ "second.md" ]] +} + +@test "handles multiple broken links across multiple files" { + cat > "$SITE_DIR/page1.html" < + +
  • + + + none + + +
  • + + +EOF + + cat > "$SITE_DIR/page2.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "broken1.md" ]] + [[ "$output" =~ "broken2.md" ]] +} + +@test "ignores 'none' text not in md-ellipsis context" { + cat > "$SITE_DIR/test.html" < + +

    This is none of my business

    +
    none
    + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 0 ] + [[ "$output" =~ "✅ No broken navigation links found" ]] +} + +@test "finds existing markdown files in source directory" { + mkdir -p "$SRC_DIR/concepts" + touch "$SRC_DIR/concepts/test-file.md" + + cat > "$SITE_DIR/page.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "$SRC_DIR/concepts/test-file.md" ]] +} + +@test "handles relative paths in hrefs" { + cat > "$SITE_DIR/page.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "relative.md" ]] +} + +@test "accepts custom site directory as first argument" { + CUSTOM_SITE="$TEST_DIR/custom_site" + mkdir -p "$CUSTOM_SITE" + + cat > "$CUSTOM_SITE/test.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$CUSTOM_SITE" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "broken.md" ]] +} + +@test "shows helpful fix message on failure" { + cat > "$SITE_DIR/test.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + [[ "$output" =~ "💡 Fix: Add 'title: Your Title' in YAML frontmatter" ]] +} + +@test "deduplicates markdown file references" { + cat > "$SITE_DIR/page1.html" < + +
  • + + + none + + +
  • + + +EOF + + cat > "$SITE_DIR/page2.html" < + +
  • + + + none + + +
  • + + +EOF + + run bash "$SCRIPT_PATH" "$SITE_DIR" "$SRC_DIR" + [ "$status" -eq 1 ] + # Should only show same-file.md once + count=$(echo "$output" | grep -c "same-file.md" || true) + [ "$count" -eq 1 ] +} diff --git a/.github/tests/test_summarize_owasp.bats b/.github/tests/test_summarize_owasp.bats new file mode 100644 index 0000000..473c464 --- /dev/null +++ b/.github/tests/test_summarize_owasp.bats @@ -0,0 +1,78 @@ +#!/usr/bin/env bats + +SCRIPT_PATH=".github/scripts/summarize_owasp.sh" +load test_helper/bats-support/load +load test_helper/bats-assert/load + +setup() { + mkdir -p target/security-reports + rm -f target/security-reports/dependency-check-report.sarif + # Sauvegarde du summary d'origine + OLD_SUMMARY_FILE="${GITHUB_STEP_SUMMARY:-}" + export OLD_SUMMARY_FILE + + # Rediriger vers un summary temporaire pour les tests + export GITHUB_STEP_SUMMARY="target/summary_for_test.txt" + rm -f "$GITHUB_STEP_SUMMARY" +} + +teardown() { + rm -rf target + + # Restaurer le summary d'origine s'il existait + if [ -n "${OLD_SUMMARY_FILE:-}" ]; then + export GITHUB_STEP_SUMMARY="$OLD_SUMMARY_FILE" + else + unset GITHUB_STEP_SUMMARY + fi +} + +@test "fails and exits 1 when vulnerabilities are present" { + cat > target/security-reports/dependency-check-report.sarif <<'EOF' +{ + "runs": [{ + "results": [ + { + "ruleId": "CVE-1234", + "message": { "text": "Vulnerable library found" }, + "properties": { "cvssScore": "7.5" } + } + ] + }] +} +EOF + + run "$SCRIPT_PATH" + + assert_failure + + assert_output "::error:: OWASP Dependency Check found 1 vulnerabilities" +} + +@test "succeeds and exits 0 when no vulnerabilities found" { + # Rapport SARIF vide + cat > target/security-reports/dependency-check-report.sarif <<'EOF' +{ + "runs": [{ + "results": [] + }] +} +EOF + + run "$SCRIPT_PATH" + + assert_success + + assert_output --regexp '^$' +} + +@test "succeeds and warns when no SARIF report is present" { + # Suppression du rapport SARIF + rm -f target/security-reports/dependency-check-report.sarif + + run "$SCRIPT_PATH" + + assert_success + + assert_output "::warning:: OWASP report missing" +} diff --git a/.github/workflows/basic_code_checks.yml b/.github/workflows/basic_code_checks.yml new file mode 100644 index 0000000..26ffe97 --- /dev/null +++ b/.github/workflows/basic_code_checks.yml @@ -0,0 +1,358 @@ +--- +name: Basic Code Checks +on: # yamllint disable-line rule:truthy + pull_request: + branches: + - main + types: [opened, synchronize, reopened, ready_for_review] + workflow_dispatch: + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }}-checks + cancel-in-progress: true + +jobs: + lint: + name: Lint & Pre-Commit Checks + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Lint GitHub Actions Workflows + uses: reviewdog/action-actionlint@0d952c597ef8459f634d7145b0b044a9699e5e43 # v1.71.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-review + level: warning + filter_mode: added + + - name: Run Pre-Commit Hooks + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: --all-files + + license_compliance: + name: Sources Licensing Compliance Check + runs-on: ubuntu-latest + needs: lint + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + cache: maven + + - name: Run License Compliance Check (Validate Phase) + run: mvn validate -B + + build: + name: Build Application + runs-on: ubuntu-latest + needs: license_compliance + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Cache Maven packages + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + + - name: Build Maven project + run: mvn clean verify + + - name: Upload built jar + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: idp-core-jar + path: target/ + retention-days: 1 + + - name: Submit Maven dependencies for security scan + uses: advanced-security/maven-dependency-submission-action@b275d12641ac2d2108b2cbb7598b154ad2f2cee8 # v5.0.0 + + security_scan: + name: Security Scan (SCA + SAST) + runs-on: ubuntu-latest + needs: build + permissions: + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Download built jar + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: idp-core-jar + path: target + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + + - name: Restore Maven cache + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Cache OWASP D/C Data + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 + with: + path: ~/.m2/repository/org/owasp/dependency-check-data + key: ${{ runner.os }}-owasp-dc-data-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-owasp-dc-data- + + - name: Upload SpotBugs XML + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: spotbugs-report + path: target/spotbugsXml.xml + retention-days: 1 + + - name: Summarize SpotBugs Results + run: | + REPORT_FILE="$PWD/target/spotbugsXml.xml" + if [ ! -f "$REPORT_FILE" ]; then + printf -- "::warning:: SpotBugs XML report file not found at %s\n" "$REPORT_FILE" + else + echo "file found $REPORT_FILE" + BUG_COUNT=$(grep -c "> "$GITHUB_STEP_SUMMARY" + fi + + check-openapi-static-swagger-update: + name: OpenAPI Static Swagger Update Check + runs-on: ubuntu-latest + needs: build + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Download built jar + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: idp-core-jar + path: target + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + + - name: Check if static swagger file is up to date + env: + SPEC_URL_PATH: '/v3/api-docs/internal' + SERVER_PORT: 8080 + OAUTH_JWK_URI: 'http://localhost:9999/mock/jwks' + OAUTH_TOKEN_URI: 'http://localhost:9999/mock/token' + IDP_CORE_PREFIX_URL: 'http://localhost:9999/mock/idp-core' + SPRING_JPA_DATABASE_PLATFORM: 'org.hibernate.dialect.H2Dialect' + SPRING_FLYWAY_ENABLED: 'false' + SPRING_JPA_HIBERNATE_DDL_AUTO: 'none' + SPRING_DATASOURCE_URL: 'jdbc:h2:mem' + SPRING_DATASOURCE_DRIVER_CLASS_NAME: 'org.h2.Driver' + run: | + mkdir -p specs + PR_SWAGGER="$PWD/docs/src/static/swagger.yaml" + if [ ! -f "$PR_SWAGGER" ]; then + echo "Static swagger file is missing in the PR" + exit 1 + fi + cp "$PR_SWAGGER" "specs/static-swagger-declared.yaml" + echo 'Extracting OpenAPI spec from running idp-core api' + java -jar target/*.jar & + API_PID=$! + sleep 30 + curl -s "http://localhost:$SERVER_PORT$SPEC_URL_PATH" > "specs/swagger-extracted.yaml" + kill "$API_PID" + + - name: Upload static swagger declared by developer + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: swagger-declared + path: specs + retention-days: 1 + + - name: Run oasdiff via Docker + run: | + printf 'Running breaking change check between static swagger declared in openapi directory and extracted swagger generated by spring boot\n' + docker run --rm -v "$PWD/specs:/specs" tufin/oasdiff:latest \ + breaking /specs/static-swagger-declared.yaml /specs/swagger-extracted.yaml \ + --format markdown --fail-on=ERR \ + > "oasdiff-report.md" + cat oasdiff-report.md + + check-breaking-changes: + name: OpenAPI Breaking Change Check + runs-on: ubuntu-latest + needs: check-openapi-static-swagger-update + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + cache: maven + + - name: Download swagger declared artifact + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: swagger-declared + path: specs + + - name: Make scripts executable + run: chmod +x .github/scripts/extract_specs.sh + + - name: Extract OpenAPI Specs from Static Swagger Files + run: .github/scripts/extract_specs.sh + + - name: Run oasdiff - Breaking Change Check + id: breaking_check + continue-on-error: true + run: | + set +e # Prevents the script from stopping at the first failure + REPORT_FILE="oasdiff-breaking-report.md" + printf -- "Running breaking change check...\n" + docker run --rm -v "$PWD/specs:/specs" tufin/oasdiff:latest \ + breaking /specs/static-swagger-main.yaml /specs/static-swagger-declared.yaml \ + --format markdown --fail-on=ERR \ + > "$REPORT_FILE" + EXIT_CODE=$? + printf -- "Exit code: %d\n" "$EXIT_CODE" + echo "oasdiff_exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" + if [ $EXIT_CODE -ne 0 ]; then + printf -- "::error::Breaking changes detected! See the report below.\n" + echo "breaking_found=true" >> "$GITHUB_OUTPUT" + else + printf -- "No breaking changes detected.\n" + echo "breaking_found=false" >> "$GITHUB_OUTPUT" + fi + cat "$REPORT_FILE" + + - name: Prepare Breaking Change Comment + if: steps.breaking_check.outputs.breaking_found == 'true' + id: breaking_message + run: | + REPORT_FILE="oasdiff-breaking-report.md" + echo "message<> "$GITHUB_OUTPUT" + echo "### BREAKING CHANGES DETECTED" + echo "" + echo "The OpenAPI contract (\`swagger.yaml\`) has backward-incompatible changes compared to the \`main\` branch." + echo "" + echo "**Breaking Changes Report:**" + echo "
    View the report" + echo "" + echo '```markdown' + cat "$REPORT_FILE" + echo '```' + echo "
    " + echo "" + echo "---" + echo "**Action required:** Please verify whether these changes are intentional before merging." + echo "EOF_MSG" >> "$GITHUB_OUTPUT" + + - name: Comment PR on Breaking Change + if: steps.breaking_check.outputs.breaking_found == 'true' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 + with: + header: breaking-changes + message: ${{ steps.breaking_message.outputs.message }} + delete: true + + - name: Remove Breaking Change Comment (If fixed) + if: steps.breaking_check.outputs.breaking_found == 'false' && github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 + with: + header: breaking-changes + delete: true + + - name: Fail Job if Breaking Changes Found + if: steps.breaking_check.outputs.breaking_found == 'true' + run: | + EXIT_CODE="${{ steps.breaking_check.outputs.oasdiff_exit_code }}" + if [ "$EXIT_CODE" -ne 0 ]; then + # check if the commit message contains "!:" to allow breaking changes + COMMIT_SHA="${{ github.event.pull_request.head.sha || github.sha }}" + COMMIT_MESSAGE=$(git log -1 --pretty=%B "$COMMIT_SHA") + if echo "$COMMIT_MESSAGE" | grep -qE "!:|BREAKING CHANGE:" + then + printf -- "::notice::Breaking change detected by API analysis, but it was correctly announced in the commit using '!: ' or 'BREAKING CHANGE:'. semantic-release will perform a MAJOR bump." + else + printf -- "::error:: Unannounced Breaking Change! The API analysis detected a backward-incompatible change, but the commit message does not include '!: ' or 'BREAKING CHANGE:'." + printf -- "Please fix the commit message to follow the convention, e.g.: 'feat!: description' or 'feat: description\\n\\nBREAKING CHANGE: description of change'." + exit 1 + fi + fi + + docker_build_test: + name: Docker Image Build Test + runs-on: ubuntu-latest + needs: build + + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Download built jar + uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + with: + name: idp-core-jar + path: target + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # V3.12.0 + + - name: Test Docker Image Build + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 + with: + context: . + file: ./Dockerfile + tags: app:test-build + load: true + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Confirm Image Built + run: | + echo "Image 'app:test-build' successfully built locally." + docker images | grep app diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000..5692790 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,45 @@ +--- +name: "Copilot Setup Steps" + +on: # yamllint disable-line rule:truthy + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Java 25 + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: 25 + cache: maven + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.14' + + - name: Install uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0 + with: + enable-cache: false + + - name: Install dependencies + working-directory: docs + run: | + uv sync diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml new file mode 100644 index 0000000..8e15288 --- /dev/null +++ b/.github/workflows/deploy_docs.yml @@ -0,0 +1,83 @@ +--- +name: Deploy Documentation to GitHub Pages + +on: # yamllint disable-line rule:truthy + push: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/deploy_docs.yml' + pull_request: + branches: + - main + paths: + - 'docs/**' + - '.github/workflows/deploy_docs.yml' + release: + types: [created] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages-${{ github.ref }}" + cancel-in-progress: false + +jobs: + build: + name: Build Documentation + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.14' + + - name: Install uv + uses: astral-sh/setup-uv@e06108dd0aef18192324c70427afc47652e63a82 # v7.5.0 + with: + enable-cache: false + + - name: Install dependencies + working-directory: docs + run: | + uv sync + + - name: Build documentation + working-directory: docs + run: | + uv run zensical build + + - name: Check navigation links + run: | + bash .github/scripts/check_navigation.sh + + - name: Setup Pages + if: github.event_name == 'release' + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0 + + - name: Upload artifact + if: github.event_name == 'release' + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 + with: + path: 'docs/site' + + deploy: + name: Deploy to GitHub Pages + if: github.event_name == 'release' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/.github/workflows/lint_pull_request_title.yml b/.github/workflows/lint_pull_request_title.yml new file mode 100644 index 0000000..6958fff --- /dev/null +++ b/.github/workflows/lint_pull_request_title.yml @@ -0,0 +1,52 @@ +--- +name: "Lint pull request title with conventional commits format" + +on: # yamllint disable-line rule:truthy + # Trigger the workflow on pull request events + workflow_dispatch: + pull_request: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: write + statuses: write + +jobs: + lint-title: + name: Lint PR title + runs-on: ubuntu-latest + steps: + + - name: Check PR title convention + uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + docs + style + refactor + perf + test + chore + build + ci + revert + scopes: | + core + api + service + ui + database + config + security + auth + logging + metrics + deps + deps-dev diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c76a046 --- /dev/null +++ b/.gitignore @@ -0,0 +1,82 @@ +# CMake +cmake-build-*/ + +# File-based project format +*.iws + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### IDE settings ### +.idea/ +.vscode/ +!.vscode/launch.json + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar +.mvn/wrapper/maven-wrapper.properties +mvnw +mvnw.cmd + + +# Eclipse m2e generated files +# Eclipse Core +.project + +# End of https://www.toptal.com/developers/gitignore/api/maven,intellij+all,java + +.env +application-secret.* + + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +replay_pid* + +# macos loder config files +.DS_Store + +# Vale.sh +.vale/styles/Google +.vale/styles/proselint diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..4d4eab8 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,226 @@ +--- +# Example markdownlint YAML configuration with all properties set to their default value + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time +MD001: true + +# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading +MD002: + # Heading level + level: 1 + +# MD003/heading-style/header-style - Heading style +MD003: + # Heading style + style: "consistent" + +# MD004/ul-style - Unordered list style +# MD004: +# # List style +# style: "sublist" + +# MD005/list-indent - Inconsistent indentation for list items at the same level +MD005: true + +# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line +MD006: true + +# MD007/ul-indent - Unordered list indentation +MD007: true + +# MD009/no-trailing-spaces - Trailing spaces +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs - Hard tabs +MD010: + # Include code blocks + code_blocks: true + # Fenced code languages to ignore + ignore_code_languages: [] + # Number of spaces for each hard tab + spaces_per_tab: 1 + +# MD011/no-reversed-links - Reversed link syntax +MD011: true + +# MD012/no-multiple-blanks - Multiple consecutive blank lines +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length - Line length +MD013: false + +# MD014/commands-show-output - Dollar signs used before commands without showing output +MD014: true + +# MD018/no-missing-space-atx - No space after hash on atx style heading +MD018: true + +# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading +MD019: true + +# MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading +MD020: true + +# MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading +MD021: true + +# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line +MD023: true + +# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content +MD024: true + +# MD025/single-title/single-h1 - Multiple top-level headings in the same document +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation - Trailing punctuation in heading +MD026: true + +# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol +MD027: true + +# MD028/no-blanks-blockquote - Blank line inside blockquote +MD028: true + +# MD029/ol-prefix - Ordered list item prefix +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space - Spaces after list markers +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists - Lists should be surrounded by blank lines +MD032: true + +# MD033/no-inline-html - Inline HTML +MD033: false + +# MD034/no-bare-urls - Bare URL used +MD034: true + +# MD035/hr-style - Horizontal rule style +MD035: + # Horizontal rule style + style: "consistent" + +# MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD037/no-space-in-emphasis - Spaces inside emphasis markers +MD037: false + +# MD038/no-space-in-code - Spaces inside code span elements +MD038: true + +# MD039/no-space-in-links - Spaces inside link text +MD039: true + +# # MD040/fenced-code-language - Fenced code blocks should have a language specified +# MD040: +# # List of languages +# allowed_languages: [en] +# # Require language only +# language_only: false + +# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links - No empty links +MD042: true + +# # MD043/required-headings/required-headers - Required heading structure +# MD043: +# # List of headings +# headings: [ +# ] +# # List of headings +# headers: [] + +# MD044/proper-names - Proper names should have the correct capitalization +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text - Images should have alternate text (alt text) +MD045: true + +# MD046/code-block-style - Code block style +MD046: false + +# MD047/single-trailing-newline - Files should end with a single newline character +MD047: true + +# MD048/code-fence-style - Code fence style +MD048: + # Code fence style + style: "consistent" + +# MD049/emphasis-style - Emphasis style should be consistent +MD049: + # Emphasis style should be consistent + style: "consistent" + +# MD050/strong-style - Strong style should be consistent +MD050: + # Strong style should be consistent + style: "consistent" + +# MD051/link-fragments - Link fragments should be valid +MD051: true + +# MD052/reference-links-images - Reference links and images should use a label that is defined +MD052: true + +# MD053/link-image-reference-definitions - Link and image reference definitions should be needed +MD053: + # Ignored definitions + ignored_definitions: ["//"] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..202be12 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,48 @@ +--- +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace # Trims trailing whitespace + exclude: | + (?x)^( + .gitmodules| + shared\/bats-libs\/.*| + .*\.excalidraw.*| + .*\.drawio.*| + .*\.snap + )$ + - id: check-yaml # Validates YAML files + args: + - --allow-multiple-documents + - id: check-json # Validates JSON files + - id: check-case-conflict # Checks for files that would conflict in case-insensitive filesystems + - id: check-merge-conflict # Checks for files that contain merge conflict strings + - id: detect-private-key # Check for the existence of private keys + - id: check-executables-have-shebangs # Checks that executables have shebangs + exclude: | + (?x)^( + .*\.java + )$ + - id: end-of-file-fixer # Makes sure files end in a newline and only a newline + - repo: https://github.com/adrienverge/yamllint + rev: v1.37.1 + hooks: + - id: yamllint # Lints YAML files + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.47.0 + hooks: + - id: markdownlint + args: + - --config + - .markdownlint.yaml + - . + - repo: https://github.com/errata-ai/vale + rev: v3.12.0 + hooks: + - id: vale + name: vale sync + pass_filenames: false + args: [sync] + - id: vale + args: [--output=line, --minAlertLevel=error] diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000..90f6b52 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,15 @@ +StylesPath = .vale/styles + +MinAlertLevel = warning + +Packages = Google, proselint + +Vocab = IDP + +[*.md] +BasedOnStyles = Vale, Google, proselint +Google.Acronyms = NO +Google.Headings = NO +Google.We = NO +Vale.Terms = NO +Google.Will = NO diff --git a/.vale/styles/config/vocabularies/IDP/accept.txt b/.vale/styles/config/vocabularies/IDP/accept.txt new file mode 100644 index 0000000..30f696c --- /dev/null +++ b/.vale/styles/config/vocabularies/IDP/accept.txt @@ -0,0 +1,39 @@ +Aiven +APIs +Autoscaler +Datadog +Dockerfile +GCP +Github +IDP +JUnit +Kafka +Kubectl +Liveness +Mockito +Multicloud +Namespace +OAuth +PostgreSQL +Rebase +Testcontainers +Validator +Zensical +bool +camelCase +enum +kubernetes +prometheus +boolean +regex +snake_case +uv +zensical +Cloud +Opentelemetry +UUIDs +yyy +ADR +ADRs +npm +Docusaurus diff --git a/.vale/styles/config/vocabularies/IDP/reject.txt b/.vale/styles/config/vocabularies/IDP/reject.txt new file mode 100644 index 0000000..e69de29 diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..22a9d9d --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,14 @@ +--- +extends: default +ignore: | + /.git + /.vale + /.venv + /docs/.venv + /docs/site + /docs/src/static/swagger.yaml +rules: + line-length: + max: 210 + trailing-spaces: enable + indentation: enable diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..41c724d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,90 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We, as members, contributors, and leaders, pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +****. +All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity/blob/master/code_of_conduct_enforcement.md). + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][faq]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[faq]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5224d23 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# Contributing + +We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). +By participating in this project you agree to abide by its terms. + +Please refer to the documentation in the `docs/` directory for guidelines on how to contribute code, report issues, and suggest enhancements. The index page is [in the contributing section](docs/src/contributing/index.md). + +Especially, please have a look at the Developer Certificate of Origin (DCO) section. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7dd4bd0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM eclipse-temurin:25-jre-alpine + +ENV PORT=8080 + +RUN apk add --no-cache shadow && \ + groupadd -r idp-core-group && \ + useradd --no-log-init -r -g idp-core-group idp-core-user && \ + apk del shadow + +USER idp-core-user:idp-core-group + +WORKDIR /opt +COPY ./target/idp-core*.jar /opt/app.jar + +ENTRYPOINT ["java", "-jar", "/opt/app.jar"] diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000..a35eda3 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,48 @@ +# Project Governance + +This document outlines the governance structure for the **idp-core** project. While day-to-day activities are covered in [CONTRIBUTING.md](CONTRIBUTING.md) and [OWNERS.md](OWNERS.md), this document focuses on the high-level decision-making bodies and processes. + +## Steering Committee + +The Steering Committee (SC) is the primary leadership body for the **idp-core** project. It is responsible for the overall strategy, health, and sustainability of the project. + +### Composition + +The Steering Committee consists of the project's core leadership. The current members are listed in [OWNERS.md](OWNERS.md). It is mainly composed of Decathlon employees who have significant contributions and responsibilities in the project. + +### Responsibilities + +The Steering Committee is responsible for: + +- **Strategic Direction**: Defining the long-term roadmap and vision suitable for the organization's goals. +- **Governance**: Approving changes to this governance document, the Code of Conduct, and license. +- **Conflict Resolution**: Serving as the final arbitration point for disputes that cannot be resolved through community consensus. +- **Brand Management**: Managing the project's brand, domain names, and social media presence. +- **Partnerships**: Managing relationships with other projects, foundations, or companies. + +## Decision Making + +We strive to operate as a consensus-based community. However, different types of decisions require different processes. + +### 1. Lazy Consensus (Day-to-Day) + +For most technical decisions, pull requests, and documentation changes, we use **Lazy Consensus**. + +- A proposal (issue or PR) is assumed to be supported if no objections are raised. +- If a maintainer objects, they must provide an alternative path or explanation. + +### 2. Strategic Technical Decisions (ADRs) + +For significant architectural changes, adopting new technologies, or breaking changes, we use the **Architecture Decision Record (ADR)** process defined in [docs/src/contributing/adrs/index.md](docs/src/contributing/adrs/index.md). + +- **Drafting**: Anyone can draft an ADR. +- **Review**: The proposal must be reviewed by the community. +- **Approval**: Requires explicit approval from the Technical Leader. + +### 3. Code of Conduct Incidents + +Incidents reported under the [Code of Conduct](CODE_OF_CONDUCT.md) are handled confidentially by the Steering Committee. If a member of the SC is involved in the report, they must recuse themselves from the handling of that specific incident. + +## Amendments + +This governance document may be amended by a vote of the Steering Committee. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..2a8ffb0 --- /dev/null +++ b/NOTICE @@ -0,0 +1,13 @@ +Copyright (C) Decathlon + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/OWNERS.md b/OWNERS.md new file mode 100644 index 0000000..b789f1b --- /dev/null +++ b/OWNERS.md @@ -0,0 +1,29 @@ +# Maintainers + +The following people are the core maintainers of the **idp-core** project: + +| Name | GitHub | Role | +| --------------- | -------------------------------------------- | ---------------- | +| Étienne JACQUOT | [@etiennej70](https://github.com/etiennej70) | Technical leader | +| Ahmed KACI | [@akacidk](https://github.com/akacidk) | Product Manager | + +## Responsibilities + +Maintainers are responsible for: + +- Reviewing and merging pull requests +- Triage issues +- Managing releases +- Enforcing the Code of Conduct +- Setting the technical direction of the project + +## Becoming a Maintainer + +Active contributors who have demonstrated a commitment to the project may be invited to become maintainers. +We look for: + +- Consistent and high-quality contributions +- Helpful and respectful interactions with other community members +- A deep understanding of the codebase and project goals + +If you are interested in becoming a maintainer, please reach out to one of the current maintainers. diff --git a/README.md b/README.md new file mode 100644 index 0000000..79402ca --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# 🚀 idp-core + +**idp-core** is the backbone of the Internal Developer Platform. It provides a modern, scalable, and extensible backend to build your software catalog, track engineering excellence through scorecards, and empower teams with self-service actions. + +--- + +## ✨ Key Features + +- 🏗️ **Software Catalog**: Centralize all your services, resources, and their relationships. +- 📊 **Engineering Excellence**: Create scorecards to monitor and improve technical standards. +- 🛠️ **Self-Service Actions**: Automate routine tasks and empower developers. +- 🔌 **Extensible Data Integration**: Pull data from any source to enrich your platform. + +## 📚 Documentation + +Detailed documentation is available in the [docs](docs/src/index.md) folder: + +- 🚀 [Quick Start](docs/src/getting-started/quickstart.md) - Get up and running in minutes. +- 📖 [Core Concepts](docs/src/concepts/index.md) - Learn about entities, templates, and relations. +- ⚙️ [Features](docs/src/features/index.md) - Discover what idp-core can do for you. +- 🔌 [API Reference](docs/src/api/index.md) - OpenAPI documentation. +- 🐳 [Deployment Guide](docs/src/deployment/index.md) - Docker, K8s, and observability. +- 🤝 [Contributing](docs/src/contributing/index.md) - How to help improve idp-core. + +--- + +## 🛠️ Technology Stack + +- **Java 25** & **Spring Boot** +- **Maven** for build and dependency management +- **PostgreSQL** with Flyway for migrations +- **OpenAPI / Swagger** for API documentation +- **Zensical** for documentation diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..80fca5c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,68 @@ +# Security Policy + +## Reporting a Vulnerability + +The IDP-Core team takes security vulnerabilities seriously. We appreciate your efforts to responsibly disclose your findings and will make every effort to acknowledge your contributions. + +### How to Report a Security Vulnerability + +**Please do NOT report security vulnerabilities through public GitHub issues.** + +Instead, please report them through one of the following channels: + +#### Option 1: GitHub Security Advisories (Recommended) + +Report vulnerabilities privately using GitHub Security Advisories: + +1. Go to the [Security tab](https://github.com/Decathlon/internal-developer-platform/security/advisories/new) of this repository +2. Fill out the advisory form with details about the vulnerability +3. Submit the advisory + +#### Option 2: email + +Send an email to **** with the following information: + +- **Subject**: Security Vulnerability in IDP-Core +- **Description**: A detailed description of the vulnerability +- **Impact**: The potential impact of the vulnerability +- **Reproduction**: Steps to reproduce the issue +- **Affected versions**: Which versions are affected +- **Suggested fix**: If you have one, please share + +### What to Expect + +When you report a vulnerability, you can expect: + +1. **Acknowledgment**: We will acknowledge receipt of your report within **3 business days** +2. **Investigation**: We will investigate and validate the report within **7 days** +3. **Updates**: We will keep you informed about the progress toward a fix +4. **Credit**: With your permission, we will publicly acknowledge your responsible disclosure once the issue is resolved + +### Response Timeline + +- **Initial Response**: Within 3 business days +- **Status Update**: Within 7 days +- **Fix Timeline**: Depends on severity + - **Critical**: Expedited release (target: 14-30 days) + - **High**: Next security release (target: 14-90 days) + - **Medium/Low**: Next regular release + +### Security Best Practices + +When deploying IDP-Core, we recommend: + +- Keep your instance updated to the latest version +- Follow the [deployment guidelines](docs/src/deployment/index.md) +- Use strong authentication mechanisms (preferred OAuth 2.0/OIDC) +- Regularly review access logs + +### Disclosure Policy + +- We follow a **coordinated disclosure** approach +- Security vulnerabilities will be disclosed publicly after a fix is available +- CVE IDs will be requested for confirmed vulnerabilities +- Credit will be given to reporters (unless anonymity is requested) + +--- + +Thank you for helping keep IDP-Core and our users safe. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..be4859d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +--- +version: "3.8" + +services: + postgres: + image: postgres:14 + environment: + POSTGRES_USER: idpcore + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-idpcore_password} + ports: + - "5437:5432" + networks: + - postgres + restart: unless-stopped + +networks: + postgres: + driver: bridge + +volumes: + postgres: diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..eaa8521 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,24 @@ +# Generated site output +site/ + +# Python virtual environment +.venv/ +venv/ +__pycache__/ +*.py[cod] +*$py.class + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Build artifacts +*.egg-info/ +dist/ +build/ diff --git a/docs/.python-version b/docs/.python-version new file mode 100644 index 0000000..6324d40 --- /dev/null +++ b/docs/.python-version @@ -0,0 +1 @@ +3.14 diff --git a/docs/pyproject.toml b/docs/pyproject.toml new file mode 100644 index 0000000..153af8d --- /dev/null +++ b/docs/pyproject.toml @@ -0,0 +1,14 @@ +[project] +name = "idp-core-docs" +version = "0.1.0" +description = "IDP-Core Documentation Site - A modern, open-source Internal Developer Platform backend" +readme = "README.md" +requires-python = ">=3.14" +dependencies = [ + "zensical>=0.0.17", + "mkdocs-swagger-ui-tag>=0.7.2", +] + +[project.scripts] +docs-serve = "zensical:serve" +docs-build = "zensical:build" diff --git a/docs/src/api/index.md b/docs/src/api/index.md new file mode 100644 index 0000000..334e81a --- /dev/null +++ b/docs/src/api/index.md @@ -0,0 +1,82 @@ +--- +title: API Reference +description: Complete REST API documentation with interactive Swagger UI +--- + +IDP-Core exposes a RESTful API for managing your software catalog. This section provides complete API documentation with interactive examples. + +## Overview + +The API follows REST conventions: + +- **Base URL**: `/api/v1` +- **Format**: JSON +- **Authentication**: Can be done by any supported configuration of Spring Boot Security (like OAuth 2.0, Basic Auth) + +## Interactive Documentation + +Explore the API interactively using Swagger UI: + + + +
    +
    +
    +
    Loading API documentation…
    +
    +
    + + + +--- + +## Next Steps + +- **[Getting Started](../getting-started/quickstart.md)** - Try your first API calls locally +- **[Data Integration](../features/data-integration.md)** - See how to ingest data +- **[Self-Service Actions](../features/self-service-actions.md)** - Trigger workflows via API diff --git a/docs/src/concepts/entities.md b/docs/src/concepts/entities.md new file mode 100644 index 0000000..4a78945 --- /dev/null +++ b/docs/src/concepts/entities.md @@ -0,0 +1,171 @@ +--- +title: Entities +description: Understand Entities - instances of Entity Templates with actual data +--- + +Entities are **instances** of Entity Templates containing actual data. If an Entity Template is the blueprint, an Entity is the house built from that blueprint. + +## Overview + +An Entity contains: + +- **Identity** - Unique identifier and title +- **Template Reference** - Which template it instantiates +- **Properties** - Actual values for the template's property definitions +- **Relations** - Links to other entities +- **Audit Fields** - Creation/modification timestamps and actors + +```mermaid +flowchart LR + subgraph Template["Entity Template: sonar_project"] + PT[properties_definitions] + RT[relations_definitions] + end + + subgraph Entity["Entity: my-backend-project"] + PE[properties: project_name, loc, issues] + RE[relations: github_repository] + end + + Template -->|instantiates| Entity +``` + +--- + +## Structure + +### Complete Example + +Here's an entity instantiated from the `sonar_project` template: + +```json +{ + "identifier": "decathlon_my-backend-project", + "title": "My Backend Project", + "template": "sonar_project", + "properties": { + "project_name": "My Backend Project", + "last_analysis_date": "2025-11-28T12:20:38+0000", + "issues_number": 137, + "loc": 20000 + }, + "relations": { + "github_repository": "my-backend-repo" + }, + "created_at": "2024-10-25T09:44:02.742Z", + "created_by": "1EOn3KYVK6L8Bh6Sm0dZ1AdG1AtAZmWt", + "updated_at": "2025-11-29T09:44:03.448Z", + "updated_by": "1EOn3KYVK6L8Bh6Sm0dZ1AdG1AtAZmWt" +} +``` + +--- + +## Core Fields + +| Field | Type | Description | +| ------------ | -------- | -------------------------------------------- | +| `identifier` | String | Unique identifier for this entity | +| `title` | String | Human-readable name | +| `template` | String | The Entity Template this entity instantiates | +| `properties` | Object | Key-value pairs of property data | +| `relations` | Object | Links to other entities | +| `created_at` | DateTime | When the entity was created | +| `created_by` | String | Who created the entity | +| `updated_at` | DateTime | Last modification time | +| `updated_by` | String | Who last modified the entity | + +--- + +## Properties + +Properties contain the actual data values. The structure follows the template's property definitions: + +```json +{ + "properties": { + "project_name": "My Backend Project", // STRING + "issues_number": 137, // NUMBER + "loc": 20000, // NUMBER + "last_analysis_date": "2025-11-28..." // STRING (date-time) + } +} +``` + +### Validation + +System validates values against the template's property rules: + +- Required properties must be present +- Types must match: STRING, NUMBER, or BOOLEAN +- Enforcement rules apply: min or max length, format, enum values + +--- + +## Relations + +Relations link entities together, forming a graph. It references the entity identifiers of related entities. + +### One-to-One Relations (`to_many: false`) + +For consistency, even single relations are represented as arrays: + +```json +{ + "relations": { + "owned_by": ["platform-team"] + } +} +``` + +### One-to-Many Relations (`to_many: true`) + +When multiple related entities are allowed, you can list several identifiers in the relation array: + +```json +{ + "relations": { + "components": ["frontend", "backend", "database"] + } +} +``` + +--- + +## Audit Fields + +Every entity tracks who created/modified it and when: + +```json +{ + "created_at": "2024-10-25T09:44:02.742Z", + "created_by": "auth0|65c1d23377c9bea7d7adc415", + "updated_at": "2025-11-29T09:44:03.448Z", + "updated_by": "webhook_integration_sonar" +} +``` + +The `created_by` and `updated_by` fields contain: + +- User IDs for manual operations +- Integration IDs for automated data ingestion + +--- + +## Dynamic Schema + +Because templates are configured at runtime, the entity structure is **dynamic**: + +> [!WARNING] +> The second-level JSON paths (`properties`, `relations`) are **not guaranteed by the API contract**. Their structure depends on the template configuration. +> +> This means: +> +> - Properties change when templates change +> - Clients should handle unknown properties gracefully + +## Next Steps + +- **[Properties](properties.md)** - Property types and validation rules +- **[Relations](relations.md)** - How entities connect +- **[Calculated Properties](calculated-properties.md)** - Automatic computations diff --git a/docs/src/concepts/entity-templates.md b/docs/src/concepts/entity-templates.md new file mode 100644 index 0000000..e4ce595 --- /dev/null +++ b/docs/src/concepts/entity-templates.md @@ -0,0 +1,235 @@ +--- +title: Entity Templates +description: Define the structure of your data with Entity Templates - the blueprints for your software catalog +--- + +Entity Templates are the **blueprints** that define the structure of entities in IDP-Core. Think of them as runtime-configurable database schemas that describe what data your platform can store. + +## Overview + +An Entity Template defines: + +- **Identity** - Unique identifier and human-readable name used to reference the template throughout the system. +- **Properties** - Data fields with types and validation rules +- **Relations** - Connections to other entity templates + +```mermaid +classDiagram + class EntityTemplate { + +String identifier + +String title + +String description + +List~PropertyDefinition~ properties + +List~RelationDefinition~ relations + } + + class PropertyDefinition { + +String name + +String description + +PropertyType type + +Boolean required + +PropertyRules rules + } + + class RelationDefinition { + +String name + +String target_entity_identifier + +Boolean required + +Boolean to_many + } + + EntityTemplate --> PropertyDefinition + EntityTemplate --> RelationDefinition +``` + +--- + +## Structure + +### Complete Example + +Here's a complete Entity Template for a Sonar project: + +```json +{ + "identifier": "sonar_project", + "title": "Sonar Project", + "description": "Data coming from Sonar about main indicators of software quality", + "properties_definitions": [ + { + "name": "project_name", + "description": "Name of the project in Sonar", + "type": "STRING", + "required": true, + "rules": { + "max_length": 200, + "min_length": 1 + } + }, + { + "name": "last_analysis_date", + "description": "Last date of analysis of the project in Sonar", + "type": "STRING", + "required": true, + "rules": { + "regex": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$" + } + }, + { + "name": "issues_number", + "description": "Number of open issues in Sonar", + "type": "NUMBER", + "required": false, + "rules": { + "max_value": 2000, + "min_value": 0 + } + }, + { + "name": "loc", + "description": "Number of lines of code", + "type": "NUMBER", + "required": false, + "rules": { + "min_value": 0 + } + } + ], + "relations_definitions": [ + { + "name": "depends_on", + "target_entity_identifier": "github_repository", + "required": true, + "to_many": false + } + ] +} +``` + +--- + +## Core Identity Fields + +### Identifier Best Practices + +- Use `snake_case` for identifiers +- Keep them short but descriptive +- Make them unique across your entire data model +- Don't change identifiers after entities are created + +```text +✅ Good: service, github_repository, sonar_project +❌ Bad: Service, GitHub Repository, my-service-template +``` + +--- + +## Properties + +Properties define the data model—the fields that entities will contain. + +See **[Properties](properties.md)** for detailed documentation. + +```json +{ + "properties_definitions": [ + { + "name": "project_name", + "description": "Name of the project", + "type": "STRING", + "required": true, + "rules": { + "min_length": 1, + "max_length": 200 + } + } + ] +} +``` + +--- + +## Relations + +Relations define how entity templates connect to each other, forming a graph structure. + +See **[Relations](relations.md)** for detailed documentation. + +```json +{ + "relations_definitions": [ + { + "name": "owned_by", + "target_entity_identifier": "team", + "required": true, + "to_many": false + }, + { + "name": "components", + "target_entity_identifier": "component", + "required": false, + "to_many": true + } + ] +} +``` + +--- + +## Best Practices + +### 1. Start Simple + +Begin with basic templates and add complexity as needed: + +```json +// Start with this +{ + "identifier": "service", + "properties_definitions": [ + {"name": "name", "type": "STRING", "required": true} + ] +} + +// Evolve to this over time +{ + "identifier": "service", + "properties_definitions": [...], + "relations_definitions": [...] +} +``` + +### 2. Use Consistent Naming + +- **Templates**: Use snake_case nouns like `github_repository`, `sonar_project` +- **Properties**: Use snake_case like `project_name`, `last_analysis_date` +- **Relations**: Use verb phrases like `owned_by`, `depends_on`, `contains` + +### 3. Document Everything + +Always include descriptions for templates and properties to help you and your team understand what each element represents. This makes maintenance and updates easier. + +```json +{ + "identifier": "sonar_project", + "description": "Represents a project analyzed by SonarQube/SonarCloud", + "properties_definitions": [ + { + "name": "coverage", + "description": "Test coverage percentage (0-100)", + "type": "NUMBER" + } + ] +} +``` + +### 4. Plan Relations Carefully + +Think about your data model before creating templates. Don't over complicate relations: you can start with simple properties and add relations later as needed. + +--- + +## Next Steps + +- **[Properties](properties.md)** - Define data fields with validation +- **[Relations](relations.md)** - Connect templates together diff --git a/docs/src/concepts/index.md b/docs/src/concepts/index.md new file mode 100644 index 0000000..16ed391 --- /dev/null +++ b/docs/src/concepts/index.md @@ -0,0 +1,126 @@ +--- +title: Core Concepts +description: Understand the fundamental concepts of IDP-Core - Entity Templates, Entities, Properties, Relations +--- + +IDP-Core sits at the center of a flexible, runtime-configurable data model. This section explains the fundamental concepts you need to understand. + +## Overview + +```mermaid +graph TB + subgraph "Data Model" + ET[Entity Template] + E[Entity] + PD[Property Definition] + PR[Property Rules] + RD[Relation Definition] + end + + ET --> PD + ET --> RD + PD --> PR + ET -.->|instantiates| E +``` + +
    + +- 📄 **[Entity Templates](entity-templates.md)** + + --- + + Blueprints that define the structure of your entities—like database schemas defined at runtime. + +- 🗄️ **[Entities](entities.md)** + + --- + + Instances of templates with actual data—your software catalog items. + +- 📋 **[Properties](properties.md)** + + --- + + Data fields with types, validation rules, and constraints. + +- 🔗 **[Relations](relations.md)** + + --- + + Connections between entities forming a knowledge graph. + +
    + +--- + +## The IDP-Core Data Model + +Unlike traditional Configuration Management Databases with rigid, predefined schemas, IDP-Core provides a **flexible meta-modeling engine**. You define your own data models—called **Entity Templates**—that mirror your organization's specific needs. + +### Key Principles + +1. **Runtime Configurable** - Create and modify data models without code changes or deployments +2. **Schema-less Flexibility** - No predefined schemas; adapt to your organization's needs +3. **Graph-Based Relations** - Connect entities to form a knowledge graph of your tech landscape + +### Example: Software Catalog + +Here's how you might model a basic software catalog: + +```mermaid +erDiagram + TEAM ||--o{ COMPONENT : owns + COMPONENT ||--o{ DEPLOYMENT : "deployed as" + COMPONENT ||--|| REPOSITORY : "has code in" + REPOSITORY ||--o{ PULL_REQUEST : contains + COMPONENT ||--o{ SONAR_PROJECT : "analyzed by" + + TEAM { + string identifier PK + string name + string slack_channel + } + + COMPONENT { + string identifier PK + string name + string status + } + + REPOSITORY { + string identifier PK + string url + number stars + } +``` + +--- + +## Quick Reference + +| Concept | What It Is | Example | +| ------------------- | --------------------------------------------------- | --------------------------------------------------------- | +| **Entity Template** | Blueprint/schema | `service`, `team`, `repository` | +| **Entity** | Instance based on a template that contains the data | `payment-service`, `platform-team`, `idp-core-repository` | +| **Property** | Data field | `name`, `status`, `url` | +| **Property Rules** | Validation | `format: EMAIL`, `min_value: 0` | +| **Relation** | Link to another entity | `owned_by`, `depends_on` | + +--- + +## How It All Fits Together + +1. **Define Templates** - Create Entity Templates that describe your domain objects +2. **Add Properties** - Specify what data each entity type contains +3. **Configure Relations** - Link templates to form a graph structure +4. **Create Entities** - Instantiate templates with actual data + +--- + +## Next Steps + +Dive deeper into each concept: + +- **[Entity Templates](entity-templates.md)** - Learn how to design your data model +- **[Properties](properties.md)** - Understand property types and validation +- **[Relations](relations.md)** - Connect your entities into a graph diff --git a/docs/src/concepts/properties.md b/docs/src/concepts/properties.md new file mode 100644 index 0000000..5463b4c --- /dev/null +++ b/docs/src/concepts/properties.md @@ -0,0 +1,400 @@ +--- +title: Properties +description: Define data fields with Property Definitions and validation rules +--- + +Properties define the **data fields** that entities must contain. Each property needs a type, optional validation rules, and can be required or optional. + +## Overview + +A Property Definition specifies: + +- **Name** - Internal identifier for the property +- **Type** - Data type (STRING, NUMBER, BOOLEAN) +- **Required** - Whether the property must have a value +- **Rules** - Validation constraints such as format, length, range, or enum values + +```mermaid +classDiagram + class PropertyDefinition { + +String name + +String description + +PropertyType type + +Boolean required + +PropertyRules rules + } + + class PropertyRules { + +PropertyFormat format + +List~String~ enum_values + +String regex + +Integer min_length + +Integer max_length + +Number min_value + +Number max_value + } + + class PropertyType { + <> + STRING + NUMBER + BOOLEAN + } + + class PropertyFormat { + <> + URL + EMAIL + } + + PropertyDefinition --> PropertyRules + PropertyDefinition --> PropertyType + PropertyRules --> PropertyFormat +``` + +--- + +## Property Definition Structure + +```json +{ + "name": "email", + "description": "Contact email address", + "type": "STRING", + "required": true, + "rules": { + "pattern": "EMAIL" + } +} +``` + +| Field | Type | Required | Description | +| ------------- | ----------- | -------- | --------------------------------------------- | +| `name` | String | Yes | Internal property name | +| `description` | String | No | Human-readable description | +| `type` | Enumeration | Yes | Data type: STRING, NUMBER, or BOOLEAN | +| `required` | Boolean | No | Whether value is mandatory (default: `false`) | +| `rules` | Object | No | Validation rules | + +--- + +## Property Types + +### STRING + +Text values. Use for names, descriptions, URLs, dates, and any text data. + +```json +{ + "name": "description", + "type": "STRING", + "required": false, + "rules": { + "min_length": 10, + "max_length": 1000, + "pattern": "^[A-Za-z0-9 ,.?!'-]+$" + } +} +``` + +### NUMBER + +Numeric values. Use for counts, metrics, scores, and measurements. + +```json +{ + "name": "coverage", + "type": "NUMBER", + "required": false, + "rules": { + "min_value": 0, + "max_value": 100 + } +} +``` + +### BOOLEAN + +True/false values. Use for flags and binary states. + +```json +{ + "name": "is_public", + "type": "BOOLEAN", + "required": false +} +``` + +--- + +## Property Rules + +Rules provide validation constraints for property values. + +### Format Rules + +Validate STRING properties against common formats: + +=== "EMAIL" + + ```json + { + "name": "contact_email", + "type": "STRING", + "rules": { + "format": "EMAIL" + } + } + ``` + + Validates: `user@example.com` + +=== "URL" + + ```json + { + "name": "repository_url", + "type": "STRING", + "rules": { + "format": "URL" + } + } + ``` + + Validates: `https://github.com/org/repo` + +### Length Rules + +Constrain STRING length: + +```json +{ + "name": "name", + "type": "STRING", + "rules": { + "min_length": 2, + "max_length": 100 + } +} +``` + +### Value Range Rules + +Constrain NUMBER values: + +```json +{ + "name": "stars", + "type": "NUMBER", + "rules": { + "min_value": 0, + "max_value": 1000000 + } +} +``` + +### Enumeration Rules + +Restrict STRING to predefined values: + +```json +{ + "name": "status", + "type": "STRING", + "rules": { + "enum_values": ["development", "staging", "production", "deprecated"] + } +} +``` + +### Regex Rules + +Custom pattern validation: + +```json +{ + "name": "version", + "type": "STRING", + "rules": { + "regex": "^v?\\d+\\.\\d+\\.\\d+$" + } +} +``` + +Validates: `v1.2.3`, `1.0.0` + +--- + +## Complete Rules Reference + +| Rule | Applies To | Description | Example | +| ------ | ------------ | ------------- | --------- | +| `format` | STRING | Predefined format validation | `"format": "EMAIL"` | +| `enum_values` | STRING | Allowed values list | `"enum_values": ["a", "b"]` | +| `regex` | STRING | Custom regex pattern | `"regex": "^[A-Z]+$"` | +| `min_length` | STRING | Minimum character length | `"min_length": 1` | +| `max_length` | STRING | Maximum character length | `"max_length": 255` | +| `min_value` | NUMBER | Minimum numeric value | `"min_value": 0` | +| `max_value` | NUMBER | Maximum numeric value | `"max_value": 100` | + +--- + +## Examples + +### Service Properties + +```json +{ + "properties_definitions": [ + { + "name": "name", + "description": "Service name", + "type": "STRING", + "required": true, + "rules": { + "min_length": 2, + "max_length": 100 + } + }, + { + "name": "description", + "description": "Service description", + "type": "STRING", + "required": false, + "rules": { + "max_length": 1000 + } + }, + { + "name": "status", + "description": "Lifecycle status", + "type": "STRING", + "required": true, + "rules": { + "enum_values": ["development", "staging", "production", "deprecated"] + } + }, + { + "name": "port", + "description": "Default port number", + "type": "NUMBER", + "required": false, + "rules": { + "min_value": 1, + "max_value": 65535 + } + }, + { + "name": "is_critical", + "description": "Whether this is a critical service", + "type": "BOOLEAN", + "required": false + } + ] +} +``` + +### Repository Properties + +```json +{ + "properties_definitions": [ + { + "name": "url", + "description": "Repository URL", + "type": "STRING", + "required": true, + "rules": { + "format": "URL" + } + }, + { + "name": "stars", + "description": "GitHub stars count", + "type": "NUMBER", + "required": false, + "rules": { + "min_value": 0 + } + }, + { + "name": "language", + "description": "Primary programming language", + "type": "STRING", + "required": false, + "rules": { + "enum_values": ["Java", "Python", "TypeScript", "Go", "Rust", "Other"] + } + }, + { + "name": "is_public", + "description": "Public visibility", + "type": "BOOLEAN", + "required": false + } + ] +} +``` + +--- + +## Best Practices + +### 1. Always Add Descriptions + +```json +{ + "name": "coverage", + "description": "Test coverage percentage (0-100)", // ✅ Clear + "type": "NUMBER" +} +``` + +### 2. Use Appropriate Types + +```json +// ❌ Bad - using STRING for numeric data +{"name": "stars", "type": "STRING"} + +// ✅ Good - use NUMBER for numeric data +{"name": "stars", "type": "NUMBER"} +``` + +### 3. Add Validation Rules + +```json +// ❌ Bad - no validation +{"name": "email", "type": "STRING"} + +// ✅ Good - validates email format +{"name": "email", "type": "STRING", "rules": {"format": "EMAIL"}} +``` + +### 4. Use Enumerations for Fixed Values + +```json +// ❌ Bad - free text for status +{"name": "status", "type": "STRING"} + +// ✅ Good - constrained to valid values +{"name": "status", "type": "STRING", "rules": {"enum_values": ["active", "inactive"]}} +``` + +### 5. Be Careful with Required + +Only mark properties as required if they truly are: + +```json +// Required for identity +{"name": "name", "type": "STRING", "required": true} + +// Optional metadata +{"name": "description", "type": "STRING", "required": false} +``` + +--- + +## Next Steps + +- **[Relations](relations.md)** - Connect entities together +- **[Calculated Properties](calculated-properties.md)** - Compute derived values diff --git a/docs/src/concepts/relations.md b/docs/src/concepts/relations.md new file mode 100644 index 0000000..08fac5d --- /dev/null +++ b/docs/src/concepts/relations.md @@ -0,0 +1,223 @@ +--- +title: Relations +description: Connect entities together with Relations to form a knowledge graph +--- + +Relations define **connections between entities**, forming a graph structure that represents your technical landscape. They allow you to model ownership, dependencies, and associations between different entity types. + +## Overview + +A Relation Definition specifies: + +- **Name** - Semantic name for the relationship. It should used on frontend side to display the relation meaningfully. +- **Target** - Which entity template it connects to +- **Cardinality** - One-to-one or one-to-many +- **Required** - Whether the relation must be established at entity creation and update time + +```mermaid +graph LR + subgraph "Entity Templates" + S[Component] + T[Team] + R[Repository] + D[Deployment] + end + + S -->|owned_by| T + S -->|repository| R + S -->|deployments| D +``` + +--- + +## Relation Definition Structure + +```json +{ + "name": "owned_by", + "target_entity_identifier": "team", + "required": true, + "to_many": false +} +``` + +| Field | Type | Required | Description | Default | +| -------------------------- | ------- | -------- | -------------------------------------------| ------- | +| `name` | String | Yes | Semantic name for the relationship | `N/A` | +| `target_entity_identifier` | String | Yes | Entity template identifier to link to | `N/A` | +| `required` | Boolean | No | Whether the relation must exist | `false` | +| `to_many` | Boolean | No | `false` = one-to-one, `true` = one-to-many | `false` | + +--- + +## Cardinality + +### One-to-One (`to_many: false`) + +Each entity can link to **exactly one** target entity. + +```json +{ + "name": "owned_by", + "target_entity_identifier": "team", + "required": true, + "to_many": false +} +``` + +**Entity data:** + +```json +{ + "relations": { + "owned_by": ["platform-team"] + } +} +``` + +**Use cases:** + +- A software owned by a single team +- A Sonar project linked to one repository +- A deployment associated with one environment + +### One-to-Many (`to_many: true`) + +Each entity can link to **multiple** target entities. + +```json +{ + "name": "components", + "target_entity_identifier": "component", + "required": false, + "to_many": true +} +``` + +**Entity data:** + +```json +{ + "relations": { + "components": ["frontend", "backend", "database", "cache"] + } +} +``` + +**Use cases:** + +- A product made up of multiple components +- A repository with many pull requests +- A team that includes several members + +--- + +## Example: Complete Data Model + +Here's a complete example with multiple templates and relations: + +### Team Template + +```json +{ + "identifier": "team", + "properties_definitions": [ + {"name": "name", "type": "STRING", "required": true}, + {"name": "slack_channel", "type": "STRING", "required": false} + ] +} +``` + +### Repository Template + +```json +{ + "identifier": "github_repository", + "properties_definitions": [ + {"name": "url", "type": "STRING", "required": true}, + {"name": "stars", "type": "NUMBER", "required": false} + ] +} +``` + +### Component Template + +```json +{ + "identifier": "component", + "properties_definitions": [ + {"name": "name", "type": "STRING", "required": true}, + {"name": "status", "type": "STRING", "required": false} + ], + "relations_definitions": [ + { + "name": "owned_by", + "target_entity_identifier": "team", + "required": true, + "to_many": false + }, + { + "name": "repository", + "target_entity_identifier": "github_repository", + "required": false, + "to_many": false + }, + { + "name": "depends_on", + "target_entity_identifier": "component", + "required": false, + "to_many": true + } + ] +} +``` + +--- + +## Best Practices + +### 1. Use Semantic Names + +```json +// ✅ Good - clear meaning +{"name": "owned_by", "target_entity_identifier": "team"} +{"name": "depends_on", "target_entity_identifier": "service"} + +// ❌ Bad - unclear +{"name": "team_link", "target_entity_identifier": "team"} +{"name": "service_ref", "target_entity_identifier": "service"} +``` + +### 2. Choose Cardinality Carefully + +Think about the real-world relationship: + +- A service has **one** owning team → `to_many: false` +- A product has **many** components → `to_many: true` + +### 3. Use Required Appropriately + +Only mark relations as required if the entity is meaningless without them. It will introduce a strong validation constraints when creating or updating entities. + +```json +// Service must have an owner +{"name": "owned_by", "required": true} + +// Service may or may not have documentation +{"name": "documentation", "required": false} +``` + +### 4. Avoid Circular Dependencies + +Be careful with relations that could create cycles: + +```mermaid +graph LR + A[Service A] -->|depends_on| B[Service B] + B -->|depends_on| C[Service C] + C -.->|depends_on| A +``` + +--- + +## Next Steps diff --git a/docs/src/contributing/adrs/0001-doc-site-engine.md b/docs/src/contributing/adrs/0001-doc-site-engine.md new file mode 100644 index 0000000..32a0a4e --- /dev/null +++ b/docs/src/contributing/adrs/0001-doc-site-engine.md @@ -0,0 +1,95 @@ +# 0001 - Choose the engine for the documentation site + +* Status: Accepted +* Deciders: + * maintainers team: Étienne JACQUOT + * contributors team: Eve BERNHARD +* Consulted: Ahmed KACI +* Informed: `N/A` +* Date: 2026-01-16 + +## Context and Problem Statement + +To ease the usage and contribution to the project, we want to provide a documentation site. We need to choose the engine that will power this documentation site. +It has to rely on markdown files as source, be easy to use and maintain, and provide good performance for end users. It will be hosted on GitHub Pages. + +## Decision Drivers + +* Security +* User experience +* Complexity of implementation +* FinOps + +## Considered Options + +1. Use `Material for MkDocs` +2. Use `Docusaurus` +3. Use `Hugo` +4. Use `Zensical` + +## Decision Outcome + +Chosen option: option 4, "Use `Zensical`," because Decathlon team has already habits with `Material for MkDocs` and `Zensical` is the next supported version of it. + +### Positive Consequences + +* Faster on boarding for new contributors already familiar with Material for MkDocs +* Access to Decathlon's internal support and best practices for Zensical based on `Material for MkDocs` knowledge + +### Negative Consequences + +* Potential learning curve for external contributors unfamiliar with the ecosystem + +## Pros and Cons of the Options + +### 1. Use `Material for MkDocs` + +Material for MkDocs is a popular theme built on top of MkDocs, providing a modern and responsive documentation site with rich features out of the box. + +* Good, because it has a large community and extensive documentation (User experience, Complexity of implementation) +* Good, because it is Python-based, easy to set up with minimal configuration (Complexity of implementation) +* Good, because it provides built-in search, dark mode, and responsive design (User experience) +* Good, because it is free for basic features and self-hosted on GitHub Pages (FinOps) +* Bad, because some advanced features require the Insiders (paid) version (FinOps) +* Bad, because it relies on Python ecosystem which may conflict with Java-focused CI pipelines (Complexity of implementation) +* Bad, because it entered in maintenance mode in favor of Zensical (Security) + +### 2. Use `Docusaurus` + +Docusaurus is a static site generator developed by Meta, designed for building documentation websites with React components. + +* Good, because it provides localization out of the box (User experience) +* Good, because it has strong community support and is actively maintained by Meta (Security) +* Good, because it allows embedding React components for interactive documentation (User experience, Security) +* Bad, because it requires Node.js and npm knowledge to customize (Complexity of implementation) +* Bad, because initial setup and configuration is more complex than MkDocs-based solutions (Complexity of implementation) +* Bad, because bundle size can be larger, impacting page load performance (User experience) + +### 3. Use `Hugo` + +Hugo is a fast static site generator written in Go, known for its speed and flexibility. + +* Good, because it is extremely fast at building large documentation sites (User experience) +* Good, because it has no runtime dependencies, single binary distribution (Complexity of implementation) +* Good, because it is completely free and open source (FinOps) +* Bad, because it requires learning Go templating language for customization (Complexity of implementation) +* Bad, because documentation-specific themes require more configuration effort (Complexity of implementation) +* Bad, because the IDP core project doesn't seems to need extended capabilities of Hugo (User experience) + +### 4. Use `Zensical` + +Zensical is the new supported evolution of Material for MkDocs, providing enterprise-grade features. + +* Good, because the team already has habits with Material for MkDocs, minimizing learning curve (Complexity of implementation) +* Good, because it is Python-based, easy to set up with minimal configuration (Complexity of implementation) +* Good, because it provides built-in search, dark mode, and responsive design (User experience) +* Good, because it is free for basic features and self-hosted on GitHub Pages (FinOps) +* Bad, because external contributors may not be familiar with Zensical-specific features (Complexity of implementation) +* Bad, because documentation and community resources are smaller compared to Material for MkDocs (User experience) + +## More Information + +* [Zensical documentation](https://zensical.org/docs/get-started/) +* [Material for MkDocs documentation](https://squidfunk.github.io/mkdocs-material/) +* [Docusaurus documentation](https://docusaurus.io/docs) +* [Hugo documentation](https://gohugo.io/documentation/) diff --git a/docs/src/contributing/adrs/_template.md b/docs/src/contributing/adrs/_template.md new file mode 100644 index 0000000..f942f47 --- /dev/null +++ b/docs/src/contributing/adrs/_template.md @@ -0,0 +1,77 @@ + +# {short title of solved problem and solution} + +* Status: {"Draft" | "Proposed" | "Rejected" | "Accepted" | "Deprecated" | "Superseded by [ADR-0005](0005-example.md)"} +* Deciders: + * maintainers team: {list one or two person involved in the decision} + * contributors team: {list one or two involved in the decision - if ownership is in builders team, fill with `N/A`} +* Consulted: {list everyone whose opinions are sought (typically subject-matter experts); and with whom there is a two-way communication} +* Informed: {list everyone who is kept up-to-date on progress; and with whom there is a one-way information} +* Date: {YYYY-MM-DD when the decision was last updated} + +Technical Story: {description | ticket/issue URL} + +## Context and Problem Statement + +{Describe the context and problem statement, for example in free form using two to three sentences or in the form of an illustrative story. +You may want to articulate the problem in form of a question and add links to collaboration boards or issue management systems.} + +## Decision Drivers + + +* Security +* User experience +* Complexity of implementation +* FinOps + +## Considered Options + +1. {title of option 1} +2. {title of option 2} +3. {title of option 3} +4. … + +## Decision Outcome + +Chosen option: option 1, "{title of option 1,}" because {justification. for example only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. + +### Positive Consequences + + +* {positive consequence, for example improvement, further opportunities, positive impact on the team, …} +* … + +### Negative Consequences + +* {negative consequence, for example compromising one or more desired qualities, adding legacy, workaround to deal with later…} +* … + +## Pros and Cons of the Options + +### 1. {title of option 1} + + +{example | description | pointer to more information | …} + +* Good, because {argument a} ({decision drivers addressed}) +* Good, because {argument b} ({decision drivers addressed}) +* Bad, because {argument d} ({decision drivers addressed}) +* … + +### 2. {title of other option} + +* Good, because {argument a} ({decision drivers addressed}) +* Good, because {argument b} ({decision drivers addressed}) +* Bad, because {argument d} ({decision drivers addressed}) +* … + + +## More Information + +{You might want to provide additional evidence/confidence for the decision outcome here and/or + document the team agreement on the decision and/or + define when/how this decision the decision should be realized and if/when it should be re-visited. +Links to other decisions and resources might appear here as well. +Please link any kind of resource used for the decision (doc to the provider, other teams wiki, …)} diff --git a/docs/src/contributing/adrs/index.md b/docs/src/contributing/adrs/index.md new file mode 100644 index 0000000..7428875 --- /dev/null +++ b/docs/src/contributing/adrs/index.md @@ -0,0 +1,9 @@ +--- +title: Architecture Decision Records +description: Repository of Architecture Decision Records (ADRs) for IDP-Core. +--- + +Here are referenced the Architecture Decision Records (ADRs) documenting key architectural decisions made during the development of IDP-Core. +Each ADR captures the context, decision, and consequences of significant architectural choices. This document is required for contributors to be sent before implementing major changes to the architecture or feature of IDP-Core. + +This project is using MADR template. diff --git a/docs/src/contributing/ci-workflow.md b/docs/src/contributing/ci-workflow.md new file mode 100644 index 0000000..eb18719 --- /dev/null +++ b/docs/src/contributing/ci-workflow.md @@ -0,0 +1,69 @@ +--- +title: CI Workflow +description: Pipeline checks and build process for IDP-Core +--- + +## 📈 Visual Workflow + +```mermaid +flowchart TD + %% Triggers + StartPR([PR Created/Updated]) + StartMain([Push to Main]) + + %% PR Workflow + subgraph PR_Checks [Pull Request Checks] + direction TB + LintTitle[Lint PR Title] + Sonar[SonarCloud Analysis] + + subgraph BasicChecks [Basic Code Checks Workflow] + LintCode[Lint & Pre-Commit Hooks] --> License[License Compliance Check] + License --> UnitTests[Run Unit Tests] + UnitTests --> IntTests[Run Integration Tests] + IntTests --> BuildApp[Build Application] + BuildApp --> BreakingChange{Breaking Change?} + BreakingChange -->|Yes| CheckCommit{Announced?} + CheckCommit -->|Yes| AllowBreaking[⚠️ Allow with Warning] + CheckCommit -->|No| FailPipeline[❌ Fail Pipeline] + BreakingChange -->|No Breaking| PassCheck[✅ Pass Check] + end + end + + %% Main Workflow + subgraph Deploy_Flow [Deployment Workflow] + direction TB + BuildProd[Build Docker Image] --> TagImage[Tag with SHA & Latest] + TagImage --> PushReg[Push to Registry] + end + + %% Connections + StartPR --> LintTitle + StartPR --> Sonar + StartPR --> LintCode + + StartMain --> BuildProd + + %% Styling + classDef trigger fill:#9575cd,stroke:#333,stroke-width:2px,color:white; + classDef step fill:#e1f5fe,stroke:#01579b,stroke-width:2px; + classDef decision fill:#fff9c4,stroke:#f57f17,stroke-width:2px; + classDef success fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px; + classDef failure fill:#ffcdd2,stroke:#c62828,stroke-width:2px; + classDef container fill:#fff3e0,stroke:#ff6f00,stroke-width:2px,stroke-dasharray: 5 5; + + class StartPR,StartMain trigger; + class LintTitle,Sonar,LintCode,License,UnitTests,IntTests,BuildApp,BuildProd,TagImage,PushReg step; + class BreakingChange,CheckCommit decision; + class PassCheck,AllowBreaking success; + class FailPipeline failure; + class BasicChecks,Deploy_Flow container; +``` + +## Key Checks + +1. **Conventional Commits**: PR titles must follow `feat:`, `fix:`, etc. We also control the scope, for example `feat(domain): ...`. +2. **Breaking Changes**: We use `oasdiff` to check for API breaking changes. + * **Announce** breaking changes in the commit message, for example `feat(domain)!: remove endpoint`. + * Unannounced breaking changes **fail the pipeline**. +3. **Tests**: Unit tests and Integration tests using Testcontainers must pass. Coverage should be at least 80%. diff --git a/docs/src/contributing/code/best-practices.md b/docs/src/contributing/code/best-practices.md new file mode 100644 index 0000000..8047974 --- /dev/null +++ b/docs/src/contributing/code/best-practices.md @@ -0,0 +1,24 @@ +--- +title: Best Practices +description: Architecture, database, security, and testing best practices for IDP-Core +--- + +## 🏗️ Architecture + +* **Dependency Rule**: Domain depends on nothing. +* **Interface Segregation**: Keep interfaces focused, for example `EntityTemplateRepository` vs generic `Repository`. +* **Exceptions**: Throw business exceptions in Domain, handle them in Infrastructure. + +## 💾 Database + +* **Migrations**: Always use Flyway. Never modify existing migration files, they are cumulative and immutable. +* **JPA**: Use `FetchType.LAZY` by default. Use `JOIN FETCH` for performance only where necessary. + +## 🔒 Security + +* **Validation**: Validate at DTO level (`@Valid`) and Database level (`unique=true`). + +## 🧪 Testing + +* **Data**: Use dedicated JSON files for test data. +* **Builders**: Use Builder pattern for creating test objects. diff --git a/docs/src/contributing/code/code-conventions.md b/docs/src/contributing/code/code-conventions.md new file mode 100644 index 0000000..faade88 --- /dev/null +++ b/docs/src/contributing/code/code-conventions.md @@ -0,0 +1,225 @@ +--- +title: Code Conventions +description: Coding standards and style guide for IDP-Core +--- + +Following consistent coding conventions ensures readable, maintainable code. This guide covers all standards for IDP-Core development. + +## General Principles + +1. **Readability** - Developers read code more than they write it +2. **Simplicity** - Prefer simple solutions +3. **Consistency** - Follow existing patterns +4. **Testability** - Write testable code + +--- + +## Java Conventions + +### Naming + +| Element | Convention | Example | +| ---------- | ----------------- | ------------------------ | +| Classes | PascalCase | `EntityTemplate` | +| Interfaces | PascalCase | `EntityRepository` | +| Methods | camelCase pattern | `findById()` | +| Variables | camelCase pattern | `entityCount` | +| Constants | SCREAMING_SNAKE | `MAX_PAGE_SIZE` | +| Packages | lowercase | `com.decathlon.idp_core` | + +### Records for Data Transfer Objects + +Use Java records for immutable data transfer objects: + +```java +// ✅ Good: Record for command +public record CreateEntityTemplateCommand( + String identifier, + String title, + String description, + List properties +) {} + +// ✅ Good: Record for response +public record EntityTemplateResponse( + UUID id, + String identifier, + String title, + Instant createdAt +) {} +``` + +### Optional Usage + +```java +// ✅ Good: Return Optional for nullable results +public Optional findByIdentifier(String identifier) { + return repository.findByIdentifier(identifier); +} + +// ✅ Good: Handle Optional properly +EntityTemplate template = findByIdentifier(id) + .orElseThrow(() -> new NotFoundException(id)); + +// ❌ Bad: Optional as parameter +public void process(Optional filter) { } // Don't do this + +// ❌ Bad: Optional.get() without check +template = findByIdentifier(id).get(); // Dangerous! +``` + +### Null Safety + +```java +// ✅ Good: Validate inputs +public EntityTemplate create(CreateCommand command) { + Objects.requireNonNull(command, "command must not be null"); + Objects.requireNonNull(command.identifier(), "identifier must not be null"); + // ... +} + +// ✅ Good: Use annotations +public @NonNull EntityTemplate findById(@NonNull EntityTemplateId id) { + // ... +} +``` + +--- + +## Exception Handling + +### Domain Exceptions + +```java +// Base domain exception +public abstract class DomainException extends RuntimeException { + private final String code; + + protected DomainException(String code, String message) { + super(message); + this.code = code; + } +} + +// Specific exceptions +public class EntityNotFoundException extends DomainException { + public EntityNotFoundException(EntityId id) { + super("ENTITY_NOT_FOUND", "Entity with id '%s' not found".formatted(id)); + } +} + +public class InvalidIdentifierException extends DomainException { + public InvalidIdentifierException(String identifier) { + super("INVALID_IDENTIFIER", "Identifier '%s' is invalid".formatted(identifier)); + } +} +``` + +### Global Exception Handler + +```java +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity handleNotFound(EntityNotFoundException ex) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(new ErrorResponse(ex.getCode(), ex.getMessage())); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidation(MethodArgumentNotValidException ex) { + var errors = ex.getBindingResult().getFieldErrors().stream() + .map(e -> e.getField() + ": " + e.getDefaultMessage()) + .toList(); + return ResponseEntity.badRequest() + .body(new ErrorResponse("VALIDATION_ERROR", String.join(", ", errors))); + } +} +``` + +--- + +## Code Formatting + +### Spotless Configuration + +Code formatting is enforced by Spotless. Run before committing: + +```bash +mvn spotless:apply +``` + +### Import Order + +```java +// Standard library +import java.util.List; +import java.util.Map; + +// Third-party +import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; + +// Project +import com.decathlon.idp_core.domain.model.EntityTemplate; +``` + +### Line Length + +- Maximum 120 characters +- Break long lines at logical points + +```java +// ✅ Good: Break at parameters +public EntityTemplate create( + String identifier, + String title, + List properties) { + // ... +} + +// ✅ Good: Break at method chain +return entities.stream() + .filter(e -> e.isActive()) + .map(this::toResponse) + .collect(toList()); +``` + +--- + +## Documentation + +### Java Documentation + +```java +/** + * Creates a new entity template with the given specification. + * + *

    The identifier must be unique across all templates and follow + * the pattern: lowercase letters, numbers, hyphens, and underscores. + * + * @param command the creation command containing template details + * @return the created entity template + * @throws DuplicateIdentifierException if identifier already exists + * @throws InvalidIdentifierException if identifier format is invalid + */ +public EntityTemplate create(CreateCommand command) { + // ... +} +``` + +### When to Document + +- ✅ Public API methods +- ✅ Complex business logic +- ✅ Non-obvious implementations +- ❌ Obvious getters/setters +- ❌ Self-explanatory code + +--- + +## Next Steps + +- **[Testing](../testing.md)** - Testing conventions +- **[Pull Requests](../pull-requests.md)** - PR guidelines diff --git a/docs/src/contributing/code/domain-infrastructure.md b/docs/src/contributing/code/domain-infrastructure.md new file mode 100644 index 0000000..15b20bc --- /dev/null +++ b/docs/src/contributing/code/domain-infrastructure.md @@ -0,0 +1,42 @@ +# Domain-Infrastructure Separation + +## The Golden Rule + +**The Domain Layer must never depend on the Infrastructure Layer.** + +## Domain Layer Details + +Pure Java implementation without Spring annotations for basic domain logic. + +### 📂 Structure + +```text +domain/ +├── configuration/ # Configuration classes for domain-specific settings +├── constant/ # Constants used across the domain layer +├── exception/ # Custom exceptions for domain-specific errors +├── model/ # Domain models representing core business entities +├── service/ # Domain services containing business logic +└── repository/ # Port interfaces defining repository contracts +``` + +## Infrastructure Layer (`com.decathlon.idp_core.infrastructure`) + +Everything technical: spring Boot, database, REST, external APIs. + +### 📂 Infrastructure Structure + +```text +infrastructure/ +├── configuration/ # Spring Config, Security, Swagger +├── controller/ # rest Controllers, API endpoints +├── dto/ # Data Transfer Objects for mapping +├── handler/ # Global exception handlers +└── mapper/ # Object mappers for converting between Domain and DTOs +``` + +### Data Flow + +1. **Controller**: Receives DTO, validates, and maps to Domain. +2. **Service**: Executes Business Logic. +3. **Repository**: Persists Domain Entity via JPA implementation. diff --git a/docs/src/contributing/code/domain-model-validations.md b/docs/src/contributing/code/domain-model-validations.md new file mode 100644 index 0000000..f544527 --- /dev/null +++ b/docs/src/contributing/code/domain-model-validations.md @@ -0,0 +1,27 @@ +--- +title: Domain Model Validations +description: Domain model validation strategies and best practices for IDP-Core +--- + +## Validation Strategy + +We use Hibernate Validator library at two levels: + +1. **Data Transfer Objects**: Structure and format validation, for example `@NotBlank`, `@Email`. +2. **Entities**: Business rule validation and database constraints. + +## Key Annotations + +| Annotation | Usage | +| ---------------------- | ---------------------------------------------- | +| `@NotBlank` | Strings must not be empty. | +| `@NotNull` | Required fields. | +| `@Min` / `@Max` | Numeric ranges. | +| `@Valid` | Trigger nested validation on objects or lists. | +| `@Column(unique=true)` | Database-level uniqueness, handled by DB. | + +## Business Rules + +* **Identifiers**: Must be unique. +* **Properties**: Must have unique names within a template. +* **Relations**: Must reference existing templates (no circular dependencies). diff --git a/docs/src/contributing/code/exception-handling.md b/docs/src/contributing/code/exception-handling.md new file mode 100644 index 0000000..72a9aae --- /dev/null +++ b/docs/src/contributing/code/exception-handling.md @@ -0,0 +1,33 @@ +--- +title: Exception Handling Strategy +description: Global exception handling strategy and error response formats for IDP-Core +--- + +## Strategy + +1. **Domain**: Throw specific business exceptions, for example `EntityTemplateNotFoundException`. +2. **Infrastructure**: Catch exceptions in `ApiExceptionHandler` with `@ControllerAdvice` annotation. +3. **API**: Return consistent JSON error responses. + +## Exception Mapping + +| Exception Type | HTTP Status | Description | +| -------------------------------------- | ------------------ | -------------------- | +| `EntityTemplateNotFoundException` | 404 Not Found | Entity not found | +| `EntityTemplateAlreadyExistsException` | 409 Conflict | Duplicate identifier | +| `ConstraintViolationException` | 400 Bad Request | Validation failed | +| `MethodArgumentNotValidException` | 400 Bad Request | Invalid request body | +| `HttpMessageNotReadableException` | 400 Bad Request | JSON parsing error | +| `Exception` | 500 Internal Error | Unexpected error | + +## Error Response Format + +At API level, always follow the same error response structure: + +```json +{ + "error": "NOT_FOUND", + "error_description": "Template with ID 'invalid-id' not found", + "timestamp": "2025-11-28T10:30:00Z" +} +``` diff --git a/docs/src/contributing/code/index.md b/docs/src/contributing/code/index.md new file mode 100644 index 0000000..0756258 --- /dev/null +++ b/docs/src/contributing/code/index.md @@ -0,0 +1,75 @@ +--- +title: Code Architecture Overview +description: Understanding the architecture and design principles of IDP-Core +--- + +IDP Core is a Spring Boot app built following the principles of the **Hexagonal Architecture**. + +## Key Technologies + +- **Spring Boot 3.5.6** +- **Spring Security** +- **Spring Data JPA** & **PostgreSQL** +- **Docker** & **Testcontainers library** +- **Flyway** + +## Sections + +

    + +- 🏢 [**Domain & Infrastructure**](domain-infrastructure.md) + + Separation of concerns and folder structure. + +- ⚠️ [**Exception Handling**](exception-handling.md) + + Global strategy and error response formats. + +- ✅ [**Validations**](domain-model-validations.md) + + DTO vs Entity validation rules. + +- 🎨 [**Code Conventions**](code-conventions.md) + + Coding standards and style guide. + +- ⭐ [**Best Practices**](best-practices.md) + + Checklist for architecture, DB, and security. + +
    + +## Architecture Principles + +We strictly separate the **Domain** (Business Logic) from the **Infrastructure** (Technical concerns). + +### Hexagonal Architecture + +```mermaid +flowchart LR + A[Infrastructure - Input Ports] --> B[Domain - Core] + B --> C[Infrastructure - Output Ports] + + subgraph A[Infrastructure - Input Ports] + A1[REST API] + A2[Controllers] + A3[DTOs] + end + + subgraph B[Domain - Core] + B1[Entities] + B2[Services] + B3[Exceptions] + end + + subgraph C[Infrastructure - Output Ports] + C1[Database] + C2[Repositories] + C3[External APIs] + end +``` + +### Core Rules + +1. **Dependency Rule**: Infrastructure depends on Domain. Domain depends on **nothing**. +2. **Testing**: Domain is unit-tested. Infrastructure is integration-tested in addition. diff --git a/docs/src/contributing/development-setup.md b/docs/src/contributing/development-setup.md new file mode 100644 index 0000000..5839e2c --- /dev/null +++ b/docs/src/contributing/development-setup.md @@ -0,0 +1,162 @@ +--- +title: Development Setup +description: Set up your local development environment for IDP-Core +--- + +This guide walks you through setting up a complete development environment for IDP-Core. + +## Prerequisites + +| Tool | Version | Purpose | +| ------ | ------- | --------------- | +| Java | 25+ | Runtime | +| Maven | 3.9+ | Build tool | +| Docker | 20.10+ | Local services | +| Git | 2.30+ | Version control | +| IDE | - | Development | + +### Verify Installation + +```bash +java -version # Should show 25+ +mvn -version # Should show 3.9+ +docker --version +git --version +pre-commit --version # If using pre-commit (recommended) +``` + +--- + +## Clone Repository + +```bash +# Clone your fork +git clone https://github.com/YOUR_USERNAME/internal-developer-platform.git +cd internal-developer-platform + +# Add upstream remote +git remote add upstream https://github.com/Decathlon/internal-developer-platform.git +``` + +--- + +## IDE Setup + +### VS Code (Recommended) + +1. Install [VS Code](https://code.visualstudio.com/) +2. Install extensions: + - Java Extension Pack + - Maven for Java + - Docker +3. Open the project folder in VS Code +4. Import Maven projects when prompted + +--- + +## Start the project + +For your development setup, you can refer to the Getting started documentation here: [Getting Started](getting-started.md) + +### Pre-Commit Hooks (Optional) + +Install pre-commit hooks for code quality: + +```bash +pre-commit install +``` + +--- + +## Database Setup + +### Automatic Migration + +Flyway runs migrations automatically on startup. Check the migrations in: + +```bash +src/main/resources/db/migration/ +├── V1_1__Create_property_rules_table.sql +├── V1_2__Create_property_definition_table.sql +├── V1_3__Create_relation_definition_table.sql +├── V1_4__Create_entity_template_table.sql +└── V1_5__Create_junction_tables.sql +``` + +### Sample Data + +For local development, the system inserts sample data: + +```bash +src/main/resources/db/local/ +└── R__1_Insert_sample_data.sql +``` + +### Reset Database + +```bash +# Drop and recreate +docker compose down -v +docker compose up -d postgres + +# Or manually +psql -c "DROP DATABASE idp; CREATE DATABASE idp;" +``` + +--- + +## Development Workflow + +### 1. Sync with Upstream + +```bash +git fetch upstream +git checkout main +git merge upstream/main +``` + +### 2. Create Feature Branch + +```bash +git checkout -b feature/my-feature +``` + +### 3. Make Changes + +Edit code, write tests. + +### 4. Run Checks + +```bash +# Run tests +mvn test -Dspring.profiles.active=test + +# Check for issues +mvn verify + +#Run pre-commit hooks (if installed) +git add . +pre-commit run --all-files +``` + +### 5. Commit + +```bash +git add . +git commit -m "feat: add my feature" +``` + +### 6. Push & Create PR + +```bash +git push origin feature/my-feature +# Open PR on GitHub +``` + +--- + +## Next Steps + +- **[Architecture](architecture.md)** - Understand the codebase +- **[Code Conventions](code/code-conventions.md)** - Follow coding standards +- **[Testing](testing.md)** - Write effective tests diff --git a/docs/src/contributing/documentation.md b/docs/src/contributing/documentation.md new file mode 100644 index 0000000..7676945 --- /dev/null +++ b/docs/src/contributing/documentation.md @@ -0,0 +1,360 @@ +--- +title: Documentation +description: Contributing to IDP-Core documentation +--- + +Good documentation is essential for IDP-Core's success. This guide explains how to contribute to the docs. + +## Documentation Stack + +| Tool | Purpose | +| -------- | ------------------------------------------ | +| Zensical | Documentation generator, MkDocs-compatible | +| Markdown | Content format | +| Mermaid | Diagrams | + +--- + +## Local Setup + +### Prerequisites + +- Python 3.14+ +- uv package manager + +### Install Dependencies + +```bash +cd docs + +# Install dependencies +uv sync + +# Or with pip +pip install -e . + +source .venv/bin/activate +``` + +### Run Locally + +```bash +# Start dev server +uv run zensical serve + +# Or simply +zensical serve +``` + +Open + +### Build Static Site + +```bash +zensical build +``` + +Output in `site/` directory. + +--- + +## Directory Structure + +```bash +doc-site/ +├── docs/ +│ ├── index.md # Homepage +│ ├── getting-started/ # Getting started guides +│ │ ├── index.md +│ │ ├── installation.md +│ │ ├── quickstart.md +│ │ └── configuration.md +│ ├── concepts/ # Core concepts +│ ├── features/ # Feature documentation +│ ├── api/ # API reference +│ ├── deployment/ # Deployment guides +│ └── contributing/ # Contributor guides +├── zensical.toml # Site configuration +└── pyproject.toml # Python dependencies +``` + +--- + +## Writing Guidelines + +### Page Structure + +```markdown +--- +title: Page Title +description: Brief description for SEO +--- + +Introduction paragraph explaining the topic. + +## Section 1 + +Content... + +## Section 2 + +Content... + +--- + +## Next Steps + +- **[Related Page](related.md)** - Brief description +``` + +### Headings + +- `#` - Page title, one per page +- `##` - Major sections +- `###` - Subsections for detailed topics +- `####` - Use rarely for fine details + +### Writing Style + +Writing style and prose is linted using Vale with the "Google" style guide and integrate specific vocabulary for IDP-Core. + +| Do | Don't | +| ----------------------- | ----------------- | +| Use active voice | Use passive voice | +| Be concise | Be verbose | +| Use present tense | Use future tense | +| Address reader as "you" | Use "the user" | + +**Examples:** + +```markdown +✅ "Create an entity template by calling the API." +❌ "An entity template can be created by the user by calling the API." + +✅ "Run the following command:" +❌ "The following command should be run:" +``` + +--- + +## Formatting + +### Code Blocks + +````markdown +```bash +mvn spring-boot:run +``` + +```java +public class Example { + // Code here +} +``` + +```yaml title="application.yml" +spring: + profiles: + active: local +``` +```` + +### Tables + +```markdown +| Column 1 | Column 2 | Column 3 | +|----------|----------|----------| +| Value 1 | Value 2 | Value 3 | +``` + +### Admonitions + +We use native Markdown admonitions instead of Zensical-specific syntax. + +```markdown +> [!NOTE] +> This is a note. + +> [!WARNING] +> This is a warning. + +> [!DANGER] +> This is dangerous. +``` + +### Tabs + +```markdown +=== "Java" + ```java + System.out.println("Hello"); + ``` + +=== "Python" + ```python + print("Hello") + ``` +``` + +--- + +## Diagrams + +### Mermaid + +````markdown +```mermaid +flowchart LR + A[Start] --> B[Process] + B --> C[End] +``` +```` + +### Diagram Types + +| Type | Use Case | +| ----------------- | ---------------- | +| `flowchart` | Process flows | +| `sequenceDiagram` | Interactions | +| `erDiagram` | Data models | +| `gantt` | Timelines | +| `classDiagram` | Class structures | + +### Examples + +#### Flowchart + +````markdown +```mermaid +flowchart TB + A[User] --> B[API] + B --> C{Valid?} + C -->|Yes| D[Process] + C -->|No| E[Error] +``` +```` + +#### Sequence Diagram + +````markdown +```mermaid +sequenceDiagram + Client->>+API: POST /entities + API->>+DB: Insert + DB-->>-API: Success + API-->>-Client: 201 Created +``` +```` + +--- + +## Navigation + +### Adding Pages + +1. Create the markdown file in appropriate directory +2. Add to `nav` in `zensical.toml`: + +```toml +[[nav]] +title = "Section" +items = [ + { title = "Page Title", path = "section/page.md" } +] +``` + +### Internal Links + +```markdown +[Link text](../concepts/entities.md) +[Link to section](../concepts/entities.md#section-name) +``` + +### External Links + +```markdown +[External link](https://example.com) +``` + +--- + +## Images + +### Adding Images + +Place images in `docs/assets/images/`: + +```markdown +![Alt text](../assets/images/diagram.png) +``` + +### Image Guidelines + +- For diagram images, use excalidraw.svg or draw.io.svg format for VS Code extension compatibility +- Use SVG format as soon as possible, PNG as a fallback, then JPEG +- Optimize file size +- Add meaningful alt text + +--- + +## API Documentation + +### Swagger Integration + +The API reference uses embedded Swagger UI with the swagger.yaml file in docs/src/static/, automatically generated by SpringBoot at build time. + +### Updating Swagger + +1. Do your changes in the codebase +2. Run the app locally: `mvn spring-boot:run -Dspring-boot.run.profiles=local` +3. Access Swagger UI at `http://localhost:8080/swagger-ui.html` +4. Export the updated `swagger.yaml` from the UI +5. Replace the file in `docs/src/static/swagger.yaml` + +--- + +## Review Checklist + +Before submitting documentation changes: + +- [ ] Spell-checked content +- [ ] Links work correctly +- [ ] Code examples pass testing in their environment +- [ ] Diagrams render properly +- [ ] Page builds without errors +- [ ] Follows style guidelines +- [ ] Navigation updated if needed + +--- + +## Common Issues + +### Build Errors + +```bash +# Check for errors +zensical build --strict + +# Common fixes: +# - Fix broken links +# - Add missing files to nav +# - Check YAML front matter +``` + +### Broken Links + +```bash +# Find broken links +zensical build --strict 2>&1 | grep "not found" +``` + +### Missing Images + +Ensure images are in `docs/static/images/` and paths are correct. + +--- + +## Next Steps + +- **[Code Conventions](code-conventions.md)** - Code documentation standards +- **[Pull Requests](pull-requests.md)** - Submit your changes diff --git a/docs/src/contributing/index.md b/docs/src/contributing/index.md new file mode 100644 index 0000000..7e69d36 --- /dev/null +++ b/docs/src/contributing/index.md @@ -0,0 +1,93 @@ +--- +title: Contributing +description: Guide for contributing to IDP-Core development +--- + +Thank you for your interest in contributing to IDP-Core. This section provides everything you need to get started as a contributor. + +## Ways to Contribute + +| Contribution | Description | +| -------------------- | ------------------------------------ | +| 🐛 Bug Reports | Report issues you've found | +| 💡 Feature Requests | Suggest new features | +| 📖 Documentation | Improve or translate docs | +| 🧪 Testing | Add tests or improve coverage | +| 💻 Code | Fix bugs or implement features | +| 🔍 Code Review | Review pull requests | +| 💬 Discussions | Participate in community discussions | + +--- + +## Getting Help + +- **GitHub Discussions** - Questions and ideas +- **GitHub Issues** - Bug reports and feature requests + +--- + +## Sections + +
    + +- 🌿 **[Architecture](code/index.md)** + + Understand the codebase structure + +- 🛠️ **[Development Setup](development-setup.md)** + + Set up your local environment + +- 📝 **[Pull Requests](pull-requests.md)** + + PR guidelines and process + +- 🧪 **[Testing](testing.md)** + + Testing strategies and tools + +- 📚 **[Documentation](documentation.md)** + + Contributing to docs + +- 👷 **[Build workflows](ci-workflow.md)** + + CI/CD pipeline and workflows + +
    + +--- + +## Code of Conduct + +Please carefully read and follow our **[Code of Conduct](code-of-conduct.md)** to ensure a welcoming environment for all contributors. + +### Our Standards + +- Use welcoming and inclusive language +- Be respectful of differing viewpoints +- Accept constructive criticism gracefully +- Focus on what's best for the community + +--- + +## Developer Certificate of Origin (DCO) + +By contributing to this project, you agree to abide by the terms of the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). Please sign your commits with the following statement: + +```bash +Signed-off-by: Your Name < your-email-address > +``` + +We enforce signed commits for this repository. +The email address can be set as the no-reply email of your GitHub account. You can follow this [guide](https://docs.github.com/en/enterprise-cloud@latest/account-and-profile/reference/email-addresses-reference#your-noreply-email-address) to set it up. + +--- + +## Recognition + +We recognize all contributors in: + +- GitHub contributors list +- Release notes for significant contributions +- AUTHORS file for major contributors diff --git a/docs/src/contributing/pull-requests.md b/docs/src/contributing/pull-requests.md new file mode 100644 index 0000000..861afcb --- /dev/null +++ b/docs/src/contributing/pull-requests.md @@ -0,0 +1,228 @@ +--- +title: Pull Requests +description: Guidelines for submitting pull requests to IDP-Core +--- + +This guide covers the process for submitting quality pull requests to IDP-Core. + +## Before You Start + +### Check Existing Work + +1. Search [existing issues](https://github.com/decathlon/internal-developer-platform/issues) for related work +2. Check [open PRs](https://github.com/decathlon/internal-developer-platform/pulls) for similar changes +3. For large changes, open an issue first to discuss + +### Sync Your Fork + +```bash +git fetch upstream +git checkout main +git merge upstream/main +git push origin main +``` + +--- + +## PR Workflow + +### 1. Create Feature Branch + +```bash +# From updated main +git checkout main +git pull upstream main + +# Create branch +git checkout -b feature/my-feature +``` + +### Branch Naming + +| Prefix | Purpose | Example | +| ----------- | ---------------- | --------------------------- | +| `feature/` | New features | `feature/webhook-retry` | +| `fix/` | Bug fixes | `fix/entity-validation` | +| `docs/` | Documentation | `docs/api-examples` | +| `refactor/` | Code refactoring | `refactor/repository-layer` | +| `test/` | Test additions | `test/entity-service` | + +### 2. Make Changes + +- Write clean, well-documented code +- Follow [code conventions](code-conventions.md) +- Add/update tests +- Update documentation if needed following [documentation guidelines](documentation.md) + +### 3. Commit Changes + +#### Commit Message Format + +```text +(): + + + +