feat(core): add endpoint for creating inbound webhook connector#58
feat(core): add endpoint for creating inbound webhook connector#58foukou19 wants to merge 7 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 |
| import com.decathlon.idp_core.domain.model.enums.WebhookSecurityType; | ||
| import com.decathlon.idp_core.domain.model.webhook.WebhookConnector; | ||
| import com.decathlon.idp_core.infrastructure.adapters.api.dto.in.InboundWebhookCreateDtoIn;import com.decathlon.idp_core.infrastructure.adapters.api.dto.in.InboundWebhookEntityMappingDtoIn; | ||
| import com.decathlon.idp_core.infrastructure.adapters.api.dto.in.InboundWebhookMappingDtoIn; | ||
| import com.decathlon.idp_core.infrastructure.adapters.api.dto.in.InboundWebhookSecurityContractDtoIn; |
| try { | ||
| Mac mac = Mac.getInstance("HmacSHA256"); | ||
| mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); | ||
| byte[] digest = mac.doFinal(payload); | ||
| return toHex(digest); | ||
| } catch (Exception exception) { | ||
| throw new WebhookAuthenticationException("Unable to compute HMAC signature"); | ||
| } |
| String secret = WebhookSecurityConfigurationUtils.getSecretFromEnvironment(alias); | ||
|
|
||
| String expected = prefix + signatureValidator.computeHexSha256(rawBody, secret); | ||
| if (!expected.equals(provided)) { | ||
| throw new WebhookAuthenticationException("Invalid HMAC signature"); | ||
| } |
| String expected = WebhookSecurityConfigurationUtils.getSecretFromEnvironment(alias); | ||
|
|
||
| if (!expected.equals(provided)) { | ||
| throw new WebhookAuthenticationException("Invalid static token"); | ||
| } |
| String expectedRaw = username + ":" + password; | ||
| String expected = "Basic " + Base64.getEncoder().encodeToString(expectedRaw.getBytes(StandardCharsets.UTF_8)); | ||
| if (!expected.equals(authorization)) { | ||
| throw new WebhookAuthenticationException("Invalid basic authentication credentials"); | ||
| } |
| private InboundWebhookMappingDtoOut fromEntityMappingToDto(EntityDynamicMapping mapping) { | ||
| return new InboundWebhookMappingDtoOut( | ||
| mapping.templateIdentifier(), | ||
| mapping.filter(), | ||
| new InboundWebhookEntityMappingDtoOut( | ||
| mapping.entityIdentifier(), | ||
| mapping.entityTitle(), | ||
| Map.copyOf(mapping.properties()), | ||
| Map.copyOf(mapping.relations()) | ||
| ) | ||
| ); |
| ); | ||
| } | ||
|
|
||
| private void validateRelationNameAlreadyExistInTemplate(Map<String, String> webhookMappingRelations, EntityTemplate entityTemplate) { |
There was a problem hiding this comment.
| private void validateRelationNameAlreadyExistInTemplate(Map<String, String> webhookMappingRelations, EntityTemplate entityTemplate) { | |
| private void validateRelationsExistsInTemplate(Map<String, String> webhookMappingRelations, EntityTemplate entityTemplate) { | |
| if (webhookMappingRelations == null || webhookMappingRelations.isEmpty()) { | |
| return; | |
| } | |
| List<String> unknownRelations = webhookMappingRelations.keySet().stream() | |
| .filter(relationName -> entityTemplate.relationsDefinitions().stream() | |
| .noneMatch(rd -> rd.name().equals(relationName))) | |
| .toList(); | |
| if (!unknownRelations.isEmpty()) { | |
| throw new WebhookTemplateHasNoPropertiesException( | |
| String.format("The mapping references unknown relations: %s", String.join(", ", unknownRelations)) | |
| ); | |
| } | |
| } |
| @Slf4j | ||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class InboundWebhookHandler { |
There was a problem hiding this comment.
This componet will be implemented in the Generic Camel Route. Integration to discuss.
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
| @Table(name = "webhook_template_mapping") | ||
| @Getter | ||
| @Setter | ||
| @Builder |
8274fae to
7089d04
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
7089d04 to
dc7d4fb
Compare
| id UUID PRIMARY KEY, | ||
| identifier VARCHAR(255) UNIQUE NOT NULL, | ||
| template_identifier VARCHAR(255) NOT NULL, | ||
| filter VARCHAR(255) NOT NULL, | ||
| entity_identifier VARCHAR(255) NOT NULL, | ||
| entity_title VARCHAR(255) NOT NULL, | ||
| properties JSONB NOT NULL DEFAULT '{}'::jsonb, | ||
| relations JSONB NOT NULL DEFAULT '{}'::jsonb |
| @Mapping(target = "relations", qualifiedByName = "jsonStringToMap") | ||
| EntityDynamicMapping toDomain(EntityDynamicMappingJpaEntity jpa); | ||
|
|
||
| @Mapping(target = "id", ignore = true) |
591d123 to
a276d46
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
a8b7ceb to
7c476a5
Compare
…endpoint Signed-off-by: ferial OUKOUKAS <75682459+foukou19@users.noreply.github.com> Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
2acdc2c to
06f6159
Compare
Signed-off-by: foukou19 <ferial.oukoukas@decathlon.com>
06f6159 to
cb1e7bb
Compare
|
| /// Mapping rule request for inbound webhook transformation. | ||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record EntityDynamicMappingCreateDtoIn( | ||
| @NotBlank(message = "Entity dynamic mapping identifier is mandatory") String identifier, |
There was a problem hiding this comment.
Here we should use validation message constants instead of string.
|
|
||
| /// Common fields for entity dynamic mapping requests (create and update). | ||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record EntityDynamicMappingDtoInCommonFields( |
There was a problem hiding this comment.
Here we should use validation message constants instead of string.
|
|
||
| /// Mapping rule request for inbound webhook update. | ||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record EntityDynamicMappingUpdateDtoIn( |
There was a problem hiding this comment.
Here we should use validation message constants instead of string.
| /// validated in the domain layer before the connector is persisted. | ||
| @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) | ||
| public record InboundWebhookCreateDtoIn( | ||
| @NotBlank(message = WEBHOOK_CONNECTOR_IDENTIFIER_MANDATORY) @Size(max = 255, message = "Webhook identifier must not exceed 255 characters") String identifier, |
There was a problem hiding this comment.
Here we should use validation message constants instead of string.
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| /// Entity projection section for an inbound webhook mapping. | ||
| public record InboundWebhookEntityMappingDtoIn( |
There was a problem hiding this comment.
Here we should use validation message constants instead of strings.
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| /// Security contract request payload represented as `{ type, config }`. | ||
| public record InboundWebhookSecurityContractDtoIn( |
There was a problem hiding this comment.
Here we should use validation message constants instead of strings.
|
|
||
| public void validateWebhookConnectorForCreation(WebhookConnector webhookConnector) { | ||
| validateIdentifierUniqueness(webhookConnector.identifier()); | ||
| validateTitleUniqueness(webhookConnector.title()); |
| /// @param entityTemplateId the entity template UUID to check | ||
| /// @throws EntityTemplateInUseByWebhookMappingException if template is already | ||
| /// in use | ||
| public void validateTemplateNotInUseMapping(UUID entityTemplateId) { |
| /// @param entityTemplateId the entity template UUID to check | ||
| /// @throws EntityTemplateInUseByWebhookMappingException if template is already | ||
| /// in use | ||
| public void validateTemplateNotInUseMapping(UUID entityTemplateId) { |
| import org.hibernate.annotations.JdbcTypeCode; | ||
| import org.hibernate.type.SqlTypes; | ||
|
|
||
| import lombok.*; |
There was a problem hiding this comment.
Update to have only the used elements of the library.
| } | ||
| } | ||
|
|
||
| public void validatePropertiesExistInTemplate(Map<String, String> mappingProperties, |
There was a problem hiding this comment.
Fo me this propety validations should not be made here. Instead in a EntityDynamicMappingValidationService given the fact that it more a mapping concern to check that what its beeing mapped is valid an it can use or add a method in PropertyValidationService lik validatePropertiesAgainstTemplate @evebrnd @RVANDO12
maybe to create a new metho validateAgainstTemplate(EntityTemplate template, List propertyNames)
Also we are throwing a WebhookTemplateHasNoPropertiesException in a EntitytemplateValidationService
| entityTemplateValidationService.validateTemplateExists(templateIdentifier); | ||
| EntityTemplate entityTemplate = entityTemplateService | ||
| .getEntityTemplateByIdentifier(templateIdentifier); | ||
| entityTemplateValidationService.validatePropertiesExistInTemplate( |
There was a problem hiding this comment.
This can be updated to use PropertyValidationService.validatePropertiesAgainstTemplate()
| | Method | Endpoint | Purpose | | ||
| | ------ | -------- | ------- | | ||
| | `POST` | `/webhooks/{configurationId}` | Receive an inbound event for the connector identified in the URL | | ||
| | `POST` | `/api/v1/inbound-webhooks` | Create a webhook connector configuration | |
There was a problem hiding this comment.
| | `POST` | `/api/v1/inbound-webhooks` | Create a webhook connector configuration | | |
| | `POST` | `/api/v1/inbound_webhooks` | Create a webhook connector configuration | |
From Decathlon API rules, you should prefer underscores over hyphens. Hyphens are used for ranges in URL parameters
|
|
||
| --- | ||
|
|
||
| Runtime-configurable connectors that authenticate external events and map payloads to your data model. |
There was a problem hiding this comment.
| Runtime-configurable connectors that authenticate external events and map payloads to your data model. | |
| Runtime-configurable connectors to push your data from external systems within your IDP. You can map any source in your data model in minutes! |
| description: Understand webhook connectors, security strategies, and dynamic mappings in IDP-Core | ||
| --- | ||
|
|
||
| Webhooks let external systems push JSON events to IDP-Core through a generic HTTP endpoint. You configure a webhook connector at runtime, choose a security strategy, and define mappings that translate incoming payloads into entity data with JSLT expressions. |
There was a problem hiding this comment.
| Webhooks let external systems push JSON events to IDP-Core through a generic HTTP endpoint. You configure a webhook connector at runtime, choose a security strategy, and define mappings that translate incoming payloads into entity data with JSLT expressions. | |
| Webhooks let external systems push JSON events to the Internal Developer Platform through a generic HTTP endpoint. You configure a webhook connector at runtime, choose a security strategy, and define mappings that translate incoming payloads into entity data with JSLT expressions. |
|
|
||
| A webhook connector is the runtime configuration stored by IDP-Core for one inbound integration. | ||
|
|
||
| | Field | Type | Description | |
There was a problem hiding this comment.
Please format the table
| "template": "github_repository", | ||
| "filter": ".action == \"created\" or .action == \"edited\"", | ||
| "entity": { | ||
| "identifier": ".repository.full_name | gsub(\"/\"; \"_\")", |
There was a problem hiding this comment.
Is it a JSLT expression here? Sounds more like a JQ one
|
|
||
| ### Validation Rules | ||
|
|
||
| When you create or update a connector, IDP-Core validates each mapping against the target Entity Template. |
There was a problem hiding this comment.
| When you create or update a connector, IDP-Core validates each mapping against the target Entity Template. | |
| When you create or update a connector, the IDP validates each mapping against the target Entity Template. |
Please do not use IDP-Core in the user documentation. Replace by IDP / Internal Developer Platform





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", "template": "sonar_project", "filter": ".visibility == \"private\"", "name": "Sonar Project Mapping", "description": "Maps Sonar private projects to entities", "entity": { "identifier": ".key | tostring", "title": ".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", "template": "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", "title": "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", "title": "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", "title": "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", "title": "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