feat(core): add endpoint for creating inbound webhook connector#58
feat(core): add endpoint for creating inbound webhook connector#58foukou19 wants to merge 12 commits into
Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
|
|
|
|
cb49b68 to
6e81bd7
Compare
| * @param securityType the security type to check (e.g., "BASIC_AUTH", "HMAC_SHA256") | ||
| * @return true if this strategy handles this security type | ||
| */ | ||
| boolean supports(String securityType); |
There was a problem hiding this comment.
validate security type using enum
There was a problem hiding this comment.
Pull request overview
This PR introduces inbound webhook connectors in IDP-Core: a management API to CRUD connector configurations (identifier/title, security strategy, dynamic mappings) and a generic public webhook ingestion endpoint that authenticates requests based on the stored connector security settings.
Changes:
- Add webhook connector persistence (Flyway migrations, JPA entities/repositories, Postgres adapter) and domain models/services for CRUD + validation.
- Add runtime webhook ingestion endpoint (
POST /webhooks/{configurationId}) with strategy-based security validation (HMAC, static token, basic auth, JWT bearer, none). - Add JSLT-based mapping validation plus docs and test coverage for the new behavior.
Reviewed changes
Copilot reviewed 87 out of 91 changed files in this pull request and generated 17 comments.
Show a summary per file
| File | Description |
|---|---|
| src/test/resources/integration_test/json/webhook/v1/putWebhook_409_title_already_exists.json | Test payload for update conflict (title) |
| src/test/resources/integration_test/json/webhook/v1/putWebhook_200.json | Test payload for successful update |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_409_identifier_already_exists.json | Test payload for create conflict (identifier) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_400_mappings_empty.json | Test payload for create validation (empty mappings) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_400_invalid_security_type.json | Test payload for create validation (invalid security type) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_400_invalid_jslt.json | Test payload for create validation (invalid JSLT) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_400_identifier_missing.json | Test payload for create validation (missing identifier) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_400_identifier_blank.json | Test payload for create validation (blank identifier) |
| src/test/resources/integration_test/json/webhook/v1/postWebhook_201.json | Test payload for successful create |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/WebhookSecurityValidatorDispatcherTest.java | Unit tests for runtime strategy dispatch |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/StaticTokenSecurityValidatorTest.java | Unit tests for static token runtime validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/JwtBearerSecurityValidatorTest.java | Unit tests for JWT bearer runtime validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/HmacSignatureValidatorTest.java | Unit tests for HMAC digest helper |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/creation/StaticTokenWebhookSecurityCreationValidatorTest.java | Unit tests for static token config validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/creation/JwtBearerWebhookSecurityCreationValidatorTest.java | Unit tests for JWT bearer config validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/creation/HmacSha256WebhookSecurityCreationValidatorTest.java | Unit tests for HMAC config validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/creation/BasicAuthWebhookSecurityCreationValidatorTest.java | Unit tests for basic auth config validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/BasicAuthSecurityValidatorTest.java | Unit tests for basic auth runtime validation |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/api/mapper/webhook/InboundWebhookMapperTest.java | Unit tests for API ↔ domain mapping |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/api/handler/ApiExceptionHandlerTest.java | Adds coverage for new webhook/mapping exceptions |
| src/test/java/com/decathlon/idp_core/infrastructure/adapters/api/controller/InboundWebhookManagementControllerTest.java | Integration tests for webhook connector management API |
| src/test/java/com/decathlon/idp_core/domain/service/webhook/WebhookConnectorValidationServiceTest.java | Unit tests for connector validation logic |
| src/test/java/com/decathlon/idp_core/domain/service/webhook/WebhookConnectorServiceTest.java | Unit tests for connector CRUD orchestration |
| src/test/java/com/decathlon/idp_core/domain/service/webhook/security/WebhookSecurityValidationServiceTest.java | Unit tests for security config validation service |
| src/test/java/com/decathlon/idp_core/domain/service/webhook/EntityDynamicMappingValidationServiceTest.java | Unit tests for mapping ↔ template validation |
| src/main/resources/db/migration/V4_2__create_webhook_template_mapping_table.sql | Flyway migration for connector↔template mapping table |
| src/main/resources/db/migration/V4_1__create_webhook_connector_table.sql | Flyway migration for webhook connector table |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/service/InboundWebhookHandler.java | Runtime handler: resolve connector + validate security |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/WebhookSecurityValidatorDispatcher.java | Runtime strategy dispatcher for security validators |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/WebhookSecurityConfigurationUtils.java | Shared utilities for config lookup/secret resolution |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/WebhookJwtDecoderProvider.java | Cached JWT decoder provider keyed by JWKS URI |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/StaticTokenSecurityValidator.java | Static token security strategy implementation |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/JwtBearerSecurityValidator.java | JWT bearer security strategy implementation |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/HmacSignatureValidator.java | HMAC digest helper used by HMAC strategy |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/HmacSha256SecurityValidator.java | HMAC SHA-256 security strategy implementation |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/security/BasicAuthSecurityValidator.java | Basic auth security strategy implementation |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/model/StaticTokenConfig.java | Polymorphic security config model (static token) |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/model/SecurityConfig.java | Polymorphic security config interface |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/model/JwtBearerConfig.java | Polymorphic security config model (JWT bearer) |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/model/HmacConfig.java | Polymorphic security config model (HMAC) |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/model/BasicAuthConfig.java | Polymorphic security config model (basic auth) |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/webhook/controller/InboundWebhookController.java | Public webhook ingestion endpoint |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/repository/JpaWebhookTemplateMappingRepository.java | JPA repository for mapping table |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/repository/JpaWebhookConnectorRepository.java | JPA repository for connector table |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/PostgresWebhookConnectorAdapter.java | Implements connector repository port + mapping persistence |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/model/webhook/WebhookTemplateMappingJpaEntity.java | JPA entity for mapping table |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/model/webhook/WebhookConnectorJpaEntity.java | JPA entity for connector table (JSONB columns) |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/mapper/WebhookConnectorPersistenceMapper.java | MapStruct mapping: domain ↔ JPA |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/mapper/common/WebhookConnectorJsonbHelper.java | JSONB serialization/deserialization helper |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/persistence/configuration/JpaAuditingConfiguration.java | Enables JPA auditing for created/updated timestamps |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/entity_mapping/jslt/JsltEntityMappingValidator.java | Infrastructure validator for JSLT expressions |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/mapper/webhook/InboundWebhookMapper.java | API DTO ↔ domain mapping for inbound webhooks |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/handler/ApiExceptionHandler.java | Adds exception→HTTP mappings for webhook features |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/out/webhook/InboundWebhookSecurityDtoOut.java | Outbound DTO for security type |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/out/webhook/InboundWebhookMappingDtoOut.java | Outbound DTO for mapping rule |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/out/webhook/InboundWebhookEntityMappingDtoOut.java | Outbound DTO for entity mapping |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/out/webhook/InboundWebhookDtoOut.java | Outbound DTO for connector |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/in/InboundWebhookSecurityContractDtoIn.java | Inbound DTO for {type, config} security contract |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/in/InboundWebhookMappingDtoIn.java | Inbound DTO for mapping rule |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/in/InboundWebhookEntityMappingDtoIn.java | Inbound DTO for entity mapping |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/dto/in/InboundWebhookCreateDtoIn.java | Inbound DTO for connector create/update |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/controller/InboundWebhookManagementController.java | CRUD + listing API for connectors |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/configuration/SwaggerDescription.java | Adds OpenAPI description constants |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/configuration/SwaggerConfiguration.java | Adds Page schema for connector listing |
| src/main/java/com/decathlon/idp_core/infrastructure/adapters/api/configuration/SecurityConfiguration.java | Permits /webhooks/** and disables CSRF there |
| src/main/java/com/decathlon/idp_core/domain/service/webhook/WebhookConnectorValidationService.java | Domain validation: uniqueness + mapping + security |
| src/main/java/com/decathlon/idp_core/domain/service/webhook/WebhookConnectorService.java | Domain service: connector CRUD orchestration |
| src/main/java/com/decathlon/idp_core/domain/service/webhook/security/WebhookSecurityValidationService.java | Domain service: validate security configs |
| src/main/java/com/decathlon/idp_core/domain/service/webhook/EntityDynamicMappingValidationService.java | Domain validation: mapping keys vs template + required fields |
| src/main/java/com/decathlon/idp_core/domain/service/entity_template/EntityTemplateValidationService.java | Adds template property/relation lookup validation helpers |
| src/main/java/com/decathlon/idp_core/domain/port/WebhookSecurityStrategy.java | Port contract for security strategies |
| src/main/java/com/decathlon/idp_core/domain/port/WebhookConnectorRepositoryPort.java | Port contract for connector persistence |
| src/main/java/com/decathlon/idp_core/domain/port/EntityDynamicMapperValidator.java | Port contract for mapping DSL validation |
| src/main/java/com/decathlon/idp_core/domain/model/webhook/WebhookSecurity.java | Domain model for security type + config |
| src/main/java/com/decathlon/idp_core/domain/model/webhook/WebhookConnector.java | Domain aggregate for connector configuration |
| src/main/java/com/decathlon/idp_core/domain/model/enums/WebhookSecurityType.java | Enum of supported security strategies |
| src/main/java/com/decathlon/idp_core/domain/model/entity_mapping/EntityDynamicMapping.java | Domain model for mapping rule |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookTemplateHasNoPropertiesException.java | Domain exception for invalid mapping vs template |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookSecurityConfigurationException.java | Domain exception for invalid security configuration |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookConnectorTitleAlreadyExistsException.java | Domain exception for title conflict |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookConnectorNotFoundException.java | Domain exception for missing connector |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookConnectorAlreadyExistException.java | Domain exception for identifier conflict |
| src/main/java/com/decathlon/idp_core/domain/exception/webhook/WebhookAuthenticationException.java | Domain exception for runtime auth failures |
| src/main/java/com/decathlon/idp_core/domain/exception/entity_template/RelationNameNotFoundEntityTemplateRelationsException.java | Domain exception for invalid relation name |
| src/main/java/com/decathlon/idp_core/domain/exception/entity_template/PropertyNameNotFoundEntityTemplatePropertiesException.java | Domain exception for invalid property name |
| src/main/java/com/decathlon/idp_core/domain/exception/entity_mapping/EntityDynamicMappingConfigurationException.java | Domain exception for invalid mapping DSL |
| src/main/java/com/decathlon/idp_core/domain/constant/ValidationMessages.java | Adds webhook validation message constants |
| pom.xml | Adds JSLT dependency |
| docs/zensical.toml | Adds webhooks page to docs navigation |
| docs/src/concepts/webhooks.md | New docs page describing connectors, mappings, security |
| docs/src/concepts/index.md | Links webhooks concept from the concepts index |
af478c6 to
6e81bd7
Compare
0e79bea to
a840e86
Compare
Code Coverage OverviewLanguages: Java Java / code-coverage/jacocoThe overall coverage in the branch is 89%. The coverage in the branch is 90%. Show a code coverage summary of the most impacted files.
Updated |
|
fb72b72 to
a264db8
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
a264db8 to
c400ac6
Compare
8274fae to
7089d04
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
ddcda09 to
6ba89ee
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
0c32d9d to
6b43e59
Compare
5776ebd to
5ec1fab
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
d81f5e8 to
77a17ac
Compare
5c4b332 to
429a7db
Compare
| } | ||
| } | ||
|
|
||
| public void validateRelationNameAlreadyExistInTemplate( |
There was a problem hiding this comment.
This vaidation shoud be done in tne EntityDinamycValidationService using the RelationvalidationService validateRelationsAgainstTemplate with an override if necessary.
There was a problem hiding this comment.
Also the name is missleading. It would recommend something like validateRelationNameExistsInTemplate
429a7db to
cbbccc0
Compare
|
|
||
| public void validateWebhookConnectorForCreation(WebhookConnector webhookConnector) { | ||
| validateIdentifierUniqueness(webhookConnector.identifier()); | ||
| validateTitleUniqueness(webhookConnector.name()); |
There was a problem hiding this comment.
I suggest to drop this validation so the user can change the name as for othe otehr IDP elements
cbbccc0 to
d54e59a
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
d54e59a to
e98f02b
Compare
|





feat(api): add CRUD and validation for webhook configurations
What this PR Provides
This PR introduces the API endpoints for managing (CRUD) inbound webhook configurations.
It establishes the foundation for receiving data from external systems by allowing users to define how incoming payloads should be mapped to entities.
The contract now separates the entity dynamic mappings from the webhook connector.
Mappings are created and managed independently, then referenced by the webhook connector through their identifiers. This keeps mappings reusable across connectors and decouples transformation logic from connector configuration.
The core logic of this PR includes:
POST /api/v1/entity_dynamic_mappings: To create a new mapping rule.GET /api/v1/entity_dynamic_mappings: To list all existing mappings (paginated).GET /api/v1/entity_dynamic_mappings/{identifier}: To retrieve a specific mapping.PUT /api/v1/entity_dynamic_mappings/{identifier}: To update an existing mapping.DELETE /api/v1/entity_dynamic_mappings/{identifier}: To delete a mapping.POST /api/v1/inbound_webhooks: To create a new webhook configuration.GET /api/v1/inbound_webhooks: To list all existing configurations (paginated).GET /api/v1/inbound_webhooks/{identifier}: To retrieve a specific configuration.PUT /api/v1/inbound_webhooks/{identifier}: To update an existing configuration.DELETE /api/v1/inbound_webhooks/{identifier}: To delete a configuration.mapping_identifiersarray. Each referenced mapping existence is validated before the connector is persisted.POST) or update (PUT), the backend validates the syntax of all JSLT expressions (filter,entity.identifier,entity.title,properties.*,relations.*). This prevents saving mappings with invalid transformation logic.mapping_identifierslist,enabledis automatically forced tofalse, since a connector without mappings cannot process events.securityobject expressed as{ type, config }. The supported types are:NONE: No authentication (open connector).HMAC_SHA256: Signature validation using a shared secret (e.g., GitHub, Sonar).STATIC_TOKEN: Simple authentication using a static token in a header.BASIC_AUTH: HTTP Basic authentication using a username and a secret alias.JWT_BEARER: Bearer token validation against a JWKS endpoint.Review
The reviewer must double-check these points:
How to test
1. Initial State
2. What and how to test
A. Create a Valid Entity Dynamic Mapping
POSTrequest to/api/v1/entity_dynamic_mappingswith the following JSON body:{ "identifier": "sonar_project_mapping", "entity_template_identifier": "sonar_project", "filter": ".visibility == \"private\"", "name": "Sonar Project Mapping", "description": "Maps Sonar private projects to entities", "entity": { "identifier": ".key | tostring", "name": ".name", "properties": { "project_name": ".name | tostring" }, "relations": {} } }201 Createdstatus, and the response body contains the created mapping.B. Attempt to Create a Mapping with Invalid JSLT
POSTrequest to/api/v1/entity_dynamic_mappingswith a malformed JSLT expression (e.g., a missing closing parenthesis):{ "identifier": "invalid_jslt_mapping", "entity_template_identifier": "test", "filter": ".visibility == \"private\"", "name": "Invalid JSLT Mapping", "entity": { "identifier": ".key | tostring(", "title": ".name", "properties": {}, "relations": {} } }400 Bad Requeststatus, and the error message indicates a JSLT syntax validation error.C. Create a Valid Webhook Configuration Referencing the Mapping
POSTrequest to/api/v1/inbound_webhookswith the following JSON body:{ "identifier": "sonar_webhook", "name": "Sonar Webhook", "description": "Receives events from Sonar", "enabled": true, "mapping_identifiers": ["sonar_project_mapping"], "security": { "type": "HMAC_SHA256", "config": { "header_name": "X-Sonar-Webhook-HMAC-SHA256", "secret_alias": "SONAR_WEBHOOK_SECRET" } } }201 Createdstatus, and the response body contains the created configuration with its resolved mappings.D. Attempt to Reference a Non-Existent Mapping
POSTrequest to/api/v1/inbound_webhooksreferencing an unknown mapping:{ "identifier": "webhook_unknown_mapping", "name": "Webhook Unknown Mapping", "enabled": true, "mapping_identifiers": ["does_not_exist"], "security": { "type": "NONE", "config": {} } }404 Not Foundstatus, and the error message indicates that the referenced mapping does not exist.E. Verify the Enabled Guard with Empty Mappings
POSTrequest to/api/v1/inbound_webhookswith an emptymapping_identifierslist while setting"enabled": true:{ "identifier": "no_mapping_webhook", "name": "No Mapping Webhook", "enabled": true, "mapping_identifiers": [], "security": { "type": "NONE", "config": {} } }201 Createdstatus, but the response shows"enabled": false, because a connector without mappings is forced to be disabled.F. Attempt to Create a Configuration with an Invalid Security Type
POSTrequest to/api/v1/inbound_webhookswith an unsupported security type:{ "identifier": "invalid_security_webhook", "name": "Invalid Security", "enabled": true, "mapping_identifiers": ["sonar_project_mapping"], "security": { "type": "INVALID_TYPE", "config": {} } }400 Bad Requeststatus, and the error message indicates that the security type is not supported.G. Read, Update, and Delete the Configuration
GETrequest to/api/v1/inbound_webhooks/sonar_webhook.200 OKstatus and returns the full configuration forsonar_webhook, including the resolved mappings.PUTrequest to/api/v1/inbound_webhooks/sonar_webhookwith"enabled": falseand the samemapping_identifiers.200 OKstatus. A subsequentGETrequest should show"enabled": false.DELETErequest to/api/v1/inbound_webhooks/sonar_webhook.204 No Contentstatus.GETrequest to/api/v1/inbound_webhooks/sonar_webhook.404 Not Foundstatus.Breaking changes (if any)
N/A