Important: This system propagates extension fields from DtoExtension only, not extensions from Dyno (the dynamic object system). These are two different extension mechanisms:
-
DtoExtension: Extension fields passed in Integration API requests (e.g.,
extension.characteristics.color=red) - PROPAGATED ✅ -
Dyno Extensions: Internal dynamic object extensions managed by the Dyno system - NOT PROPAGATED ❌
Additionally: Extension propagation currently works only to Private API GET requests. While you can filter by any HTTP method for Integration API requests (GET, POST, PUT, PATCH, DELETE), the extensions will only be propagated when the service makes a GET call to the Private API. Private API POST, PUT, PATCH, and DELETE calls do not support automatic extension propagation at this time.
Purpose: Automatically propagate extension fields from Integration API requests to Private API GET calls, enabling dynamic filtering and data enrichment without code changes.
📋 Table of Contents
🎯 What is the Extension Field Propagation System?
The Extension Field Propagation System is an automatic mechanism that extracts custom extension fields from Integration API requests and intelligently routes them to the appropriate Private API GET calls based on configurable rules.
Key Benefits
-
Zero Code Changes: Add new filtering capabilities without modifying service code
-
Dynamic Filtering: Clients can pass custom filters that automatically reach the right endpoints
-
Type-Safe Configuration: Use enums and class references for compile-time safety
-
Pattern Matching: Flexible rules using glob patterns for paths and extension fields
-
Conditional Logic: Apply rules only when specific conditions are met
-
Flexible HTTP Method Filtering: Filter by Integration API HTTP method (any method supported)
Scope and Limitations
Current Scope:
-
✅ Works with DtoExtension extension fields from Integration API requests
-
✅ Propagates to Private API GET requests only
-
✅ Enriches
GetFiltersobjects automatically -
✅ Can filter by any HTTP method for Integration API requests (GET, POST, PUT, PATCH, DELETE)
-
❌ Does NOT work with Dyno extensions (different system)
-
❌ Does NOT propagate to Private API POST/PUT/PATCH/DELETE calls
Example: You can configure a rule that triggers on Integration API POST requests, but the extensions will only propagate if the service internally makes a GET call to the Private API.
Real-World Example
Scenario: A client wants to filter asset characteristics by color when retrieving an asset.
Without Extension Propagation:
1. Client calls: GET /integration/assets/r1/asset?code=ASSET123&extension.characteristics.color=red
2. Integration API receives request
3. Service calls Private API: GET /assets/r1/assets/123/characteristics
4. ❌ The color filter is LOST - Private API returns ALL characteristics
With Extension Propagation:
1. Client calls: GET /integration/assets/r1/asset?code=ASSET123&extension.characteristics.color=red
2. Integration API receives request and extracts extensions
3. Extension Propagation System matches rules and enriches the call
4. Service calls Private API: GET /assets/r1/assets/123/characteristics?extension.color=red
5. ✅ Private API receives the filter and returns only RED characteristics
🔧 System Configuration
Configuration Properties
The system is controlled by two application properties:
application.yml
integrationlayer:
extension-propagation:
# Master switch - enables/disables the entire system
enabled: true # Default: true
# Default rules - enables/disables pre-configured rules
default-rule:
enabled: false # Default: false
Property Details
|
Property |
Default |
Description |
|---|---|---|
|
|
|
Master switch for the entire extension propagation system. When |
|
|
|
Enables pre-configured default rules for common endpoints (Assets, Accounts, Work Orders). Set to |
Recommendation: Keep enabled: true and default-rule.enabled: false in production. Configure specific rules for your use cases instead of relying on default rules.
Default Rules (When Enabled)
When default-rule.enabled: true, the following rules are automatically configured:
|
Entity |
Integration API Endpoint |
Private API Endpoint |
Integration API Method |
|---|---|---|---|
|
Assets |
|
|
GET |
|
Accounts |
|
|
GET |
|
Work Orders |
|
|
GET |
|
WO Attachments |
|
|
GET |
Default rules propagate all extension fields (*) without prefix stripping. For more granular control, create custom rules. These rules filter by Integration API GET requests and propagate to Private API GET calls.
📝 How to Configure Custom Rules
Step 1: Create a Configuration Class
Create a new @Configuration class that extends ExtensionPropagationConfigBase:
AssetExtensionPropagationConfig.java
package overit.geocall.integrationapirest.config;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import overit.geocall.integrationapirest.common.extension.propagation.ExtensionPropagationRegistry;
import overit.geocall.integrationapirest.common.extension.propagation.ExtensionPropagationRule;
import overit.geocall.integrationapirest.common.extension.propagation.ExtensionPropagationRuleBuilder;
import overit.geocall.integrationapirest.common.extension.propagation.config.ExtensionPropagationConfigBase;
import overit.geocall.integrationapirest.service.AssetService;
import java.util.List;
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
@Configuration
@RequiredArgsConstructor
public class AssetExtensionPropagationConfig extends ExtensionPropagationConfigBase {
private final ExtensionPropagationRegistry registry;
@PostConstruct
public void configure() {
registerRules(registry, getRules());
}
@Override
protected List<ExtensionPropagationRule> getRules() {
return List.of(
// Rule 1: Propagate characteristics filters from Integration API GET requests
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiEndpoint("/*/integration/assets/**")
.fromIntegrationApiMethod(GET) // Filter Integration API by GET method
.fromMethod(AssetService.class, "getAsset")
.stripPrefix()
.named("Asset Characteristics Propagation")
.build(),
// Rule 2: Propagate validation extensions from Integration API POST requests
// Extensions will only propagate if the service makes a GET call to Private API
ExtensionPropagationRuleBuilder.forPath("validation.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiEndpoint("/*/integration/assets/**")
.fromIntegrationApiMethod(POST) // Filter Integration API by POST method
.stripPrefix()
.named("Asset Validation Propagation")
.build()
);
}
}
Important: Extension propagation to Private APIs works only for GET requests. The .fromIntegrationApiMethod() filter controls which Integration API requests trigger the rule, but the actual propagation only happens when the service makes a GET call to the Private API. The underlying system only enriches GetFilters objects used in GET operations.
Example: You can create a rule with .fromIntegrationApiMethod(POST) to capture extensions from Integration API POST requests, but those extensions will only be propagated if the service internally calls a Private API GET endpoint.
Step 2: Understanding Rule Components
Extension Field Pattern
.forPath("characteristics.*") // Match extension.characteristics.color, extension.characteristics.size, etc.
Pattern Syntax:
-
characteristics.*- Matches single level:characteristics.color✓,characteristics.size✓,characteristics.sub.field✗ -
metadata.**- Matches multiple levels:metadata.a✓,metadata.a.b.c✓ -
*.color- Matches any prefix:characteristics.color✓,metadata.color✓ -
*- Matches all extension fields
Target Private API Endpoint
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
Supports path variables: {assetId}, {id}, {workOrderId}, etc.
Matches:
-
/assets/r1/assets/123/characteristics✓ -
/assets/r1/assets/456/characteristics✓ -
/assets/r1/assets/ABC-789/characteristics✓
Note: Only GET requests to these Private API endpoints will have extensions propagated. The endpoint must use GetFilters parameter.
Source Integration API Filter (Optional)
.fromIntegrationApiEndpoint("/*/integration/assets/**")
Pattern Syntax:
-
/assets/r1/asset- Exact match -
/assets/r1/*- Single level wildcard -
/assets/**- Multi-level wildcard -
/*/integration/assets/**- Any context path + multi-level
HTTP Method Filter (Optional, Type-Safe)
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
// Single method - filter Integration API requests by GET
.fromIntegrationApiMethod(GET)
// Multiple methods (chained) - filter Integration API by POST or PUT
.fromIntegrationApiMethod(POST)
.fromIntegrationApiMethod(PUT)
// Multiple methods (varargs) - filter Integration API by any of these methods
.fromIntegrationApiMethods(GET, POST, PUT, PATCH)
Supported HTTP Methods: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Important Distinction:
-
Integration API HTTP Method: The
.fromIntegrationApiMethod()filter controls which Integration API requests (incoming client requests) trigger the rule. You can use any HTTP method (GET, POST, PUT, PATCH, DELETE). -
Private API HTTP Method: The actual propagation to Private APIs only works for GET requests. Extensions are only added to
GetFiltersobjects.
Use Case Example: Create a rule with .fromIntegrationApiMethod(POST) to capture validation extensions from Integration API POST requests. If the service then makes a GET call to the Private API to fetch related data, those validation extensions will be propagated to that GET call.
Service Method Filter (Optional, Type-Safe)
// Type-safe (recommended)
.fromMethod(AssetService.class, "getAsset")
// String-based (backward compatible)
.fromMethod("AssetServiceImpl.getAsset")
Prefix Handling
// Strip prefix (default)
.stripPrefix() // "characteristics.color" → "color"
// Keep prefix
.keepPrefix() // "characteristics.color" → "characteristics.color"
Rule Name (Optional but Recommended)
.named("Asset Characteristics Propagation") // Helps with debugging and logging
Step 3: Common Configuration Patterns
Pattern 1: Simple Propagation (Integration API GET)
Use Case: Propagate all extension fields matching a pattern from Integration API GET requests.
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiMethod(GET) // Filter Integration API GET requests
.stripPrefix()
.build()
Example:
-
Input:
GET /integration/assets/r1/asset?code=A123&extension.characteristics.color=red -
Service calls:
GET /assets/r1/assets/123/characteristics(Private API) -
Output:
GET /assets/r1/assets/123/characteristics?extension.color=red✅
Pattern 2: Integration API POST with Private API GET Propagation
Use Case: Capture extensions from Integration API POST requests and propagate them when the service makes GET calls to Private API.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
// Capture extensions from Integration API POST requests
ExtensionPropagationRuleBuilder.forPath("validation.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiMethod(POST) // Filter Integration API POST requests
.stripPrefix()
.build()
Example:
-
Input:
POST /integration/assets/r1/assetwith body andextension.validation.strict=true -
Service internally calls:
GET /assets/r1/assets/123/characteristics(to fetch existing data) -
Output:
GET /assets/r1/assets/123/characteristics?extension.strict=true✅
Pattern 3: Multiple Integration API Methods
Use Case: Apply the same propagation rule for multiple Integration API HTTP methods.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
// Filter Integration API by GET, POST, or PUT
ExtensionPropagationRuleBuilder.forPath("filter.*")
.toPrivateApiEndpoint("/assets/r1/assets")
.fromIntegrationApiMethods(GET, POST, PUT) // Any of these Integration API methods
.stripPrefix()
.build()
Example:
-
Input:
POST /integration/assets/r1/assetwithextension.filter.status=active -
Service internally calls:
GET /assets/r1/assets(Private API) -
Output:
GET /assets/r1/assets?extension.status=active✅
Pattern 4: Conditional Propagation
Use Case: Only propagate when a specific flag is set.
ExtensionPropagationRuleBuilder.forPath("details.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/details")
.fromIntegrationApiMethod(GET)
.when(ctx -> Boolean.TRUE.equals(ctx.getExtensions().get("includeDetails")))
.stripPrefix()
.build()
Example:
-
With flag:
extension.includeDetails=true&extension.details.level=full→ Propagates ✓ -
Without flag:
extension.details.level=full→ Does NOT propagate ✗
Pattern 5: Combined Filters
Use Case: Apply multiple filters for precise control.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiEndpoint("/*/integration/assets/**") // Any asset endpoint
.fromIntegrationApiMethod(GET) // Integration API GET requests
.fromMethod(AssetService.class, "getAsset") // Only from getAsset method
.when(ctx -> Boolean.TRUE.equals(ctx.getExtensions().get("includeCharacteristics")))
.stripPrefix()
.named("Asset Characteristics Propagation")
.build()
All conditions must be met:
-
Integration API request path matches
/*/integration/assets/** -
Integration API HTTP method is
GET -
Called from
AssetService.getAsset -
Extension field
includeCharacteristics = true -
Service makes a GET call to Private API
Pattern 6: Extension Transformation
Use Case: Modify extension values before propagation.
ExtensionPropagationRuleBuilder.forPath("metadata.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/metadata")
.fromIntegrationApiMethod(GET)
.stripPrefix()
.transform(extensions -> {
// Add audit metadata
extensions.put("_source", "integration-api");
extensions.put("_timestamp", System.currentTimeMillis());
return extensions;
})
.build()
🔍 How the System Works
Architecture Flow
1. Integration API Request (any HTTP method: GET, POST, PUT, etc.)
↓
GET /integration/assets/r1/asset?code=A123&extension.characteristics.color=red
2. DtoExtensionArgumentResolver
↓
Converts query params to nested map: {"characteristics": {"color": "red"}}
(Only DtoExtension fields, NOT Dyno extensions)
3. ReactiveContextInitializerAspect
↓
Extracts extensions + metadata (path, HTTP method, service method)
Stores in Reactor Context
4. Service Layer (AssetService.getAsset)
↓
Business logic executes
5. Private API Call (MUST be GET for propagation to work)
↓
privateApiClient.getReactive("/assets/r1/assets/123/characteristics", ...)
6. ExtensionAwarePrivateApiHttpClient (Wrapper)
↓
Intercepts the call (only enriches GET operations)
7. ExtensionPropagationService
↓
- Retrieves extension context from Reactor Context
- Matches endpoint against registered rules
- Applies filters (API path, Integration API HTTP method, service method, conditions)
- Flattens nested extensions to dot notation
- Strips prefix if configured
- Applies transformations
- Enriches GetFilters with matched extensions (GET only)
8. Private API Call (Enriched GET)
↓
GET /assets/r1/assets/123/characteristics?extension.color=red
9. Private API Response
↓
Returns filtered characteristics (only red items)
Key Components
|
Component |
Responsibility |
|---|---|
|
|
Converts dot notation query params to nested maps (DtoExtension only) |
|
|
Extracts extensions and metadata, stores in Reactor Context |
|
|
Holds extension data + metadata (Integration API path, HTTP method, service method) |
|
|
Stores and retrieves propagation rules |
|
|
Defines a single propagation rule with filters (can filter by any Integration API HTTP method) |
|
|
Applies rules and enriches Private API GET calls only |
|
|
Wrapper that intercepts Private API calls (only enriches GET requests) |
|
|
Glob-style pattern matching for paths |
|
|
Type-safe enum for Integration API HTTP method filtering |
🧪 Testing Your Rules
Unit Test Example
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
@Test
void testCharacteristicsPropagation() {
// Given: A rule for characteristics from Integration API GET requests
ExtensionPropagationRule rule = ExtensionPropagationRuleBuilder
.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiMethod(GET) // Filter Integration API by GET
.stripPrefix()
.build();
// Given: Extension context with characteristics
Map<String, Object> extensions = Map.of(
"characteristics", Map.of("color", "red", "size", "large")
);
ExtensionContext context = ExtensionContext.of(
extensions,
"/integration/assets/r1/asset",
"GET", // Integration API HTTP method
"getAsset",
"AssetService"
);
// When: Checking if rule matches the Private API endpoint
boolean matches = rule.matches("/assets/r1/assets/123/characteristics", context);
// Then: Rule should match
assertThat(matches).isTrue();
// When: Extracting extensions
Map<String, Object> extracted = rule.extractExtensions(extensions);
// Then: Extensions should be flattened and prefix stripped
assertThat(extracted)
.containsEntry("color", "red")
.containsEntry("size", "large");
}
Integration Test Example
@Test
void testExtensionPropagationEndToEnd() {
// Given: Integration API GET request with extension fields (DtoExtension)
webTestClient.get()
.uri(uriBuilder -> uriBuilder
.path("/integration/assets/r1/asset")
.queryParam("code", "ASSET123")
.queryParam("extension.characteristics.color", "red")
.queryParam("extension.characteristics.size", "large")
.build())
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.characteristics[?(@.color=='red')]").exists()
.jsonPath("$.characteristics[?(@.size=='large')]").exists();
}
@Test
void testExtensionPropagationFromPost() {
// Given: Integration API POST request with extension fields
// Extensions will propagate if service makes GET calls to Private API
webTestClient.post()
.uri(uriBuilder -> uriBuilder
.path("/integration/assets/r1/asset")
.queryParam("extension.validation.strict", "true")
.build())
.bodyValue(assetData)
.exchange()
.expectStatus().isOk();
// Extensions propagated to any Private API GET calls made during processing
}
🐛 Debugging and Troubleshooting
Enable Debug Logging
application.yml
logging:
level:
overit.geocall.integrationapirest.aspect.controller.reactive: DEBUG
overit.geocall.integrationapirest.aspect.http: DEBUG
overit.geocall.integrationapirest.common.extension: DEBUG
Log Output Examples
DEBUG ReactiveContextInitializerAspect - Extracted 4 extension fields from DtoExtension
DEBUG ReactiveContextInitializerAspect - Adding extension context for /integration/assets/r1/asset (GET): 4 fields
DEBUG ExtensionPropagationService - Matching endpoint: /assets/r1/assets/123/characteristics
DEBUG ExtensionPropagationService - Applied rule 'Asset Characteristics Propagation': 2 extension fields
DEBUG ExtensionPropagationService - Enriched GetFilters with extensions: {color=red, size=large}
Common Issues
Issue 1: Extensions Not Propagating
Checklist:
-
Is
integrationlayer.extension-propagation.enabled: true? -
Is the service method annotated with
@ReactiveContextInitialize? -
Does the DTO implement
DtoExtension? (Not Dyno extensions!) -
Is the rule registered in the registry?
-
Does the Private API endpoint pattern match the actual call?
-
Is the Private API call a GET request? (POST/PUT/PATCH/DELETE not supported)
-
Does the Private API method use
GetFiltersparameter? -
Do all filters match (Integration API path, Integration API HTTP method, service method, condition)?
Issue 2: Wrong Extensions Propagated
Check:
-
Is the extension field pattern correct? (
characteristics.*vscharacteristics.**) -
Is
stripPrefix()orkeepPrefix()configured correctly? -
Are there multiple rules matching the same endpoint? (Check rule order)
Issue 3: Dyno Extensions Not Propagating
This is expected behavior!
The Extension Field Propagation System only works with DtoExtension fields from Integration API requests. Dyno extensions (internal dynamic object extensions) are not propagated by this system. They are managed separately by the Dyno framework.
Issue 4: Private API POST/PUT/PATCH/DELETE Not Receiving Extensions
This is a current limitation!
Extension propagation to Private APIs currently only works for GET requests. The system enriches GetFilters objects which are only used in GET operations.
Important: The .fromIntegrationApiMethod() filter can be set to any HTTP method (GET, POST, PUT, PATCH, DELETE) to control which Integration API requests trigger the rule. However, the actual propagation to the Private API only happens for GET calls.
Example: You can have a rule with .fromIntegrationApiMethod(POST) that captures extensions from Integration API POST requests. If the service then makes a GET call to the Private API, those extensions will be propagated. But if the service makes a POST/PUT/DELETE call to the Private API, the extensions will NOT be propagated.
Issue 5: HTTP Method Filter Not Working
Check:
-
Are you using the
HttpMethodenum (not strings)? -
Is the HTTP method being captured in the
ExtensionContext? -
Does the actual Integration API request HTTP method match the filter?
-
Remember: The filter controls Integration API requests, but propagation only works for Private API GET calls
// ✅ Correct - using enum
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
.fromIntegrationApiMethod(GET) // Filter Integration API GET requests
.fromIntegrationApiMethod(POST) // Filter Integration API POST requests
// ❌ Wrong - strings not supported
// .fromIntegrationApiMethod("GET")
Issue 6: Path Variables Not Matching
Check:
-
Is the endpoint pattern using
{variableName}syntax? -
Does the actual endpoint have a value in that position?
// Rule
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
// Actual Private API call
privateApiClient.getReactive("/assets/r1/assets/123/characteristics", ...)
// ✅ Matches - {assetId} = 123
📚 Best Practices
1. Use Descriptive Rule Names
// ✅ Good
.named("Asset Characteristics Color Filter Propagation")
// ❌ Avoid
.named("Rule1")
2. Use Type-Safe Filters
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
// ✅ Good - type-safe
.fromIntegrationApiMethod(GET)
.fromMethod(AssetService.class, "getAsset")
// ❌ Avoid - error-prone
// .fromIntegrationApiMethod("GET")
// .fromMethod("AssetServiceImpl.getAsset")
3. Understand Integration API vs Private API HTTP Methods
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
// ✅ Good - clear understanding
// This rule captures extensions from Integration API POST requests
// and propagates them to Private API GET calls
.fromIntegrationApiMethod(POST) // Integration API filter
// Private API must be GET for propagation to work
// ✅ Also good - explicit GET filter
.fromIntegrationApiMethod(GET) // Integration API GET requests
// Propagates to Private API GET calls
4. Use Pattern Matching for Flexibility
// ✅ Good - one rule for all versions
.fromIntegrationApiEndpoint("/*/integration/assets/**")
// ❌ Avoid - multiple rules for each version
.fromIntegrationApiEndpoint("/integration/assets/r1/asset")
.fromIntegrationApiEndpoint("/integration/assets/r2/asset")
5. Order Rules by Specificity
// Specific rule - higher priority (lower order number)
ExtensionPropagationRuleBuilder.forPath("characteristics.color")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.withOrder(1)
.build(),
// General rule - lower priority (higher order number)
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.withOrder(10)
.build()
6. Document Your Rules
@Override
protected List<ExtensionPropagationRule> getRules() {
return List.of(
// Rule 1: Propagate color/size filters to characteristics endpoint
// Use case: Client sends GET request with filter extensions
// Example: extension.characteristics.color=red → extension.color=red
// Note: Propagates to Private API GET calls only
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiMethod(GET)
.stripPrefix()
.named("Asset Characteristics Filter Propagation")
.build(),
// Rule 2: Propagate validation extensions from POST requests
// Use case: Client sends POST request with validation extensions
// Extensions propagate to any Private API GET calls made during processing
ExtensionPropagationRuleBuilder.forPath("validation.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiMethod(POST)
.stripPrefix()
.named("Asset Validation Propagation from POST")
.build()
);
}
7. Test Your Rules
Always write unit tests for your propagation rules to ensure they work as expected.
📖 Quick Reference
Extension Field Patterns
|
Pattern |
Matches |
Example |
|---|---|---|
|
|
Single level |
|
|
|
Multiple levels |
|
|
|
Any prefix |
|
|
|
All fields |
Any extension field |
API Path Patterns
|
Pattern |
Matches |
Example |
|---|---|---|
|
|
Exact match |
|
|
|
Single level |
|
|
|
Multiple levels |
|
|
|
Any context + multi-level |
|
HTTP Methods
|
Method |
Integration API Filter |
Private API Propagation |
|---|---|---|
|
|
✅ Supported |
✅ Supported |
|
|
✅ Supported |
❌ Not supported |
|
|
✅ Supported |
❌ Not supported |
|
|
✅ Supported |
❌ Not supported |
|
|
✅ Supported |
❌ Not supported |
|
|
✅ Supported |
❌ Not supported |
|
|
✅ Supported |
❌ Not supported |
Key Distinction:
-
Integration API Filter: The
.fromIntegrationApiMethod()can filter by any HTTP method. This controls which incoming Integration API requests trigger the rule. -
Private API Propagation: Extensions are only propagated to GET requests made to Private APIs, regardless of the Integration API HTTP method.
💡 Real-World Examples
Example 1: Asset Characteristics Filtering (Integration API GET)
Requirement: Allow clients to filter asset characteristics by color, size, and material using GET requests.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
ExtensionPropagationRuleBuilder.forPath("characteristics.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiEndpoint("/*/integration/assets/**")
.fromIntegrationApiMethod(GET) // Filter Integration API GET requests
.stripPrefix()
.named("Asset Characteristics Filter")
.build()
Usage:
Integration API Request: GET /integration/assets/r1/asset?code=A123&extension.characteristics.color=red&extension.characteristics.size=large
Service calls Private API: GET /assets/r1/assets/123/characteristics
Private API receives: GET /assets/r1/assets/123/characteristics?extension.color=red&extension.size=large ✅
Example 2: Validation Extensions from POST Requests
Requirement: Capture validation extensions from Integration API POST requests and propagate them to Private API GET calls made during processing.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
ExtensionPropagationRuleBuilder.forPath("validation.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/characteristics")
.fromIntegrationApiEndpoint("/*/integration/assets/**")
.fromIntegrationApiMethod(POST) // Filter Integration API POST requests
.stripPrefix()
.named("Asset Validation Propagation")
.build()
Usage:
Integration API Request: POST /integration/assets/r1/asset?extension.validation.strict=true (with body)
Service calls Private API: GET /assets/r1/assets/123/characteristics (to fetch existing data)
Private API receives: GET /assets/r1/assets/123/characteristics?extension.strict=true ✅
Note: If service calls POST /assets/r1/assets/123 (Private API), extensions are NOT propagated ❌
Example 3: Multiple Integration API Methods
Requirement: Apply the same filter propagation for GET, POST, and PUT Integration API requests.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
ExtensionPropagationRuleBuilder.forPath("filter.*")
.toPrivateApiEndpoint("/assets/r1/assets")
.fromIntegrationApiEndpoint("/*/integration/assets/**")
.fromIntegrationApiMethods(GET, POST, PUT) // Any of these Integration API methods
.stripPrefix()
.named("Asset Filter Propagation")
.build()
Usage:
Scenario 1 - Integration API GET:
Integration API: GET /integration/assets/r1/asset?extension.filter.status=active
Private API: GET /assets/r1/assets?extension.status=active ✅
Scenario 2 - Integration API POST:
Integration API: POST /integration/assets/r1/asset?extension.filter.status=active
Private API: GET /assets/r1/assets?extension.status=active ✅
Scenario 3 - Integration API PUT:
Integration API: PUT /integration/assets/r1/asset/123?extension.filter.status=active
Private API: GET /assets/r1/assets?extension.status=active ✅
Example 4: Conditional Detail Propagation
Requirement: Only propagate detail extensions when includeDetails=true.
import static overit.geocall.integrationapirest.common.extension.propagation.HttpMethod.*;
ExtensionPropagationRuleBuilder.forPath("details.*")
.toPrivateApiEndpoint("/assets/r1/assets/{assetId}/details")
.fromIntegrationApiMethod(GET)
.when(ctx -> Boolean.TRUE.equals(ctx.getExtensions().get("includeDetails")))
.stripPrefix()
.named("Asset Details Conditional Propagation")
.build()
Usage:
Request (with flag): GET /integration/assets/r1/asset?code=A123&extension.includeDetails=true&extension.details.level=full
Private API receives: GET /assets/r1/assets/123/details?extension.level=full ✅
Request (no flag): GET /integration/assets/r1/asset?code=A123&extension.details.level=full
Private API receives: GET /assets/r1/assets/123/details (no extensions) ✗
Need Help? If you have questions or need assistance configuring extension propagation rules, please reach out to the Integration API team.