Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Integration Engine Laravel Package

carlosgude/integration-engine

IntegrationEngine standardizes external API integrations in Symfony: each endpoint is a predictable Action with a Request and Response DTO + Mapper. Includes scaffolding commands to generate integrations and actions, keeping HTTP/mapping logic consistent and maintainable.

View on GitHub
Deep Wiki
Context7
v3.0.0

Added

  • Middleware pipeline: ClientMiddlewareInterface + AbstractClientMiddleware as the extension point for cross-cutting concerns (caching, tracing, rate limiting, etc.)
  • MiddlewareClient wraps every HTTP adapter in an ordered chain; always implements BatchClientInterface and DynamicBaseUrlClientInterface
  • CachingMiddleware replaces the former caching decorators
  • TracingMiddleware replaces the former tracing decorators (debug only)
  • Third-party middlewares can be injected by tagging services with integration_engine.middleware and an optional priority attribute (higher priority = outermost layer, same convention as Symfony event listeners)

Removed

  • TraceableClient and TraceableBatchClient — superseded by TracingMiddleware

Changed

  • Layer order is now fixed and explicit: CachingMiddleware (outermost) → user middlewares by descending priority → TracingMiddleware (debug only) → HTTP adapter
v2.3.1

fix: ensure TraceableClient and TraceableBatchClient implement DynamicBaseUrlClientInterface

  • Fixed regression where TraceableClient and TraceableBatchClient failed to support DynamicBaseUrlClientInterface, breaking per-request base URL resolution in kernel.debug mode.
  • Added corresponding tests to validate behavior and prevent future regressions.
  • Updated documentation to highlight this capability for custom client decorators.
v2.3.0

Two additive, opt-in capabilities — no breaking changes, no new required dependencies.

Added

Dynamic base URL per request For integrations without one fixed host — a multitenant app where each store/customer lives on its own domain (Shopify-style). IntegrationEngine::send() and sendMany() now accept an optional baseUrl argument:

$engine->send('GetOrders', context: $context, baseUrl: $tenant->domain());

- New DynamicBaseUrlClientInterface — opt-in via instanceof, checked at runtime. A client that doesn't implement it ignores baseUrl silently.
- Implemented by both built-in adapters: SymfonyHttpClientAdapter, GraphQLClientAdapter.
- sendMany() groups batch requests by resolved baseUrl before dispatch, so a batch mixing several target hosts still runs each group concurrently through BatchClientInterface instead of degrading to sequential sends.
- Omit baseUrl and behavior is identical to every prior version — the configured base_url is used, exactly as before.

Symfony Profiler / Debug Toolbar integration
Every outgoing call made through a configured integration now shows up automatically in the Symfony Toolbar/Profiler in dev/test — integration name, action, HTTP method, path, duration, and status or error. No configuration needed.

- TraceableClient / TraceableBatchClient decorate ClientInterface/BatchClientInterface — IntegrationEngine itself is untouched.
- Wired by IntegrationCompilerPass only when all three hold: kernel.debug is true, symfony/http-kernel's DataCollectorInterface is available, and a profiler service is actually registered (i.e. symfony/web-profiler-bundle is installed and active). In any other case — including all of prod — the real client is used unwrapped, zero overhead.
- symfony/http-kernel added to require-dev only — still zero new dependencies in require.

Docs

- README: "Dynamic base URL per request", "Symfony Profiler integration"
- ARCHITECTURE.md: §8 (why base_url is opt-in), §9 (why a decorator, not engine instrumentation)
- New docs/debugging.md

Quality

- 305 tests, 636 assertions — full suite green, including no-break regression coverage for both features.
- PHPStan max — no errors.
- Verified end-to-end against a real consuming app: two distinct live hosts for dynamic baseUrl, and the profiler panel rendering real outgoing calls (REST + GraphQL) including a real upstream error case.

Internal (no user-facing change, worth knowing if you touch IntegrationCompilerPass)

- Classes excluded from Symfony's service autodiscovery are registered as abstract placeholder definitions (tag container.excluded), not simply absent — hasDefinition() alone can't tell the difference from a real one.
- Compiler pass execution order across bundles matters: IntegrationCompilerPass now runs at priority 10 (before FrameworkBundle's ProfilerPass) so the data collector tag exists before the profiler's tagged-service scan runs.
v2.2.1

v2.2.1

Changed

  • DynamicAuthHandler extracted from IntegrationEngine — token resolution and caching logic now lives in a dedicated class, improving separation of concerns
  • Core contracts reorganised into sub-namespaces (Action/, Auth/, Client/, Mapper/, Response/) for cleaner imports
  • IntegrationGeneratorException introduced — generator filesystem errors now throw a dedicated exception instead of \RuntimeException
  • PathResolutionException::pcreError() named constructor added

Fixed

  • FakeLogger now handles non-string log levels gracefully

Internal

  • Landing page multilingual support (EN/ES) via Cloudflare Workers
v2.2.0

feat: introduce DynamicAuthHandler for improved token handling and logging

  • Add DynamicAuthHandler for centralized dynamic auth token resolution, caching, and 401 token retries.
  • Improve retry handling for batch requests with BatchTokenRetry updates.
  • Update IntegrationEngine to delegate dynamic auth logic to DynamicAuthHandler.
  • Enhance logging for token fetch, cache hits, and retries with optional logger integration.
  • Add new test cases for dynamic auth flows, including 401 retry scenarios and token field validation.
  • Add DispatchedRequest for simplifying request dispatch tracking in SymfonyHttpClientAdapter.
  • Update bundle DI to include DynamicAuthHandler in integrations.
  • Improve error messaging for unsupported auth types in ResolvesAuthHeaders.
  • Refactor token resolution and caching logic for better maintainability and readability.
  • Update documentation and tests to include dynamic auth changes.

fix: ensure FakeLogger handles non-string log levels gracefully

  • Update FakeLogger to set an empty string for non-string log levels, ensuring type consistency. fix: ensure FakeLogger handles non-string log levels gracefully
  • Update FakeLogger to set an empty string for non-string log levels, ensuring type consistency.
v2.1.0

ulti request (https://github.com/CarlosGude/integrationEngine/pull/4)

  • feat: introduce batch processing with sendMany and BatchResultSet
  • Add batch processing support to the integration engine with the new sendMany() method that returns keyed BatchResultSet objects for consolidated response handling.
  • Introduce BatchResult to encapsulate individual request outcomes, distinguishing success and failure.
  • Add EngineRequest and PreparedRequest classes to enable immutable packaging of batch requests and pre-processed requests, respectively.
  • Introduce BatchClientInterface for concurrent batch request execution; implements fallback to sequential processing when unavailable.
  • Add preliminary tests covering batch request key preservation, error handling, and partial results on failure.
  • Include mutation-tested logic for handling 401 token retries and token eviction in batches with dynamic auth.
  • Document batch design decisions, API, and behavior under BATCH-CONSOLIDATION.md.
  • Coverage: 100% MSI with tests for batch processing edge cases.
  • Future iterations to include AbstractBatchMapper for consolidated DTO responses in homogeneous batch actions.
  • feat: add AbstractBatchMapper, BatchResultCollection, and batch mapper support
  • Introduce AbstractBatchMapper for consolidated DTO responses in homogeneous batch actions.
  • Add BatchResultCollection to encapsulate batch processing results with action-based validation.
  • Provide batch mapper support with mapWith method for routing collections through mappers.
  • Add BatchMapperActionMismatchException to enforce batch action invariants.
  • Update IntegrationEngine::sendMany to return BatchResultCollection for unified handling of batch responses.
  • Add comprehensive test coverage for batch mappers, collection behavior, and exception scenarios.
  • test: add comprehensive sad path test coverage for batch processing, dynamic auth, and GraphQL client adapters
  • Add BatchSendSadPathTest to validate partial results, action mismatches, and retry behavior.
  • Add DynamicAuthSadPathTest to test token invalidation, custom headers, and non-401 error handling.
  • Add GraphQLClientAdapterBodyTest for query serialization, data extraction, and adapter-specific behavior.
  • Add GraphQLClientAdapterErrorTest to validate GraphQL and HTTP error handling in payload and response codes.
  • Ensure all edge cases covered for failure scenarios across components.
  • refactor: replace token retry logic in IntegrationEngine with BatchTokenRetry for improved clarity and modularity

  • feat: introduce BatchTokenRetry to manage token caching and 401 retry logic

  • Add BatchTokenRetry class for handling dynamic auth tokens in batch processing.
  • Manage token caching with eviction on 401 responses and prevent retries for fresh tokens.
  • Improve clarity, modularity, and maintainability in token retry logic.
  • chore: remove outdated BATCH-CONSOLIDATION.md, update batch docs and diagrams
  • Delete BATCH-CONSOLIDATION.md to reflect completed batch mapper implementation and prevent confusion.
  • Update batch-related documentation (batch-requests.md, clients.md) with finalized API and behavior.
  • Refine Mermaid class diagrams (class-graph.md) to incorporate batch components and processing flow.
  • Enhance TESTING.md with batch test coverage details and execution guidelines for batch-specific suites.
  • chore: remove redundant mutation testing logs
  • Delete infection-per-mutator.md and infection-summary.log as they are no longer maintained.
  • Reflect streamlined workflows relying on existing CI mutation testing outputs.
v2.0.0

🚀 v2.0.0 -- Dynamic Auth Hardening & Test Suite Expansion

🔐 Authentication & 401 Resilience

  • Added delete() to CachePort and Psr6CacheAdapter (key sanitization consistent with get/set).
  • Token handling is now self-healing:
    • Cached token returning 401 before TTL expiry is evicted and retried once with a fresh token.
    • Second 401 propagates immediately (no retry loop).
    • Non-401 errors never invalidate cached tokens.
  • Token invalidation is now fully internalized (consumers no longer responsible for cache hygiene).

🧠 Dynamic Authorization Improvements

  • Removed header name normalization in configuration (fixes X-TenantX_Tenant issue).
  • DynamicAuthorizationConfig::prefix is now nullable with smart defaults:
    • Authorization → defaults to Bearer
    • Custom headers → no prefix unless explicitly defined
  • Fixed api_key handling to correctly respect configured prefixes (including YAML static configs).

🧪 Testing Overhaul

  • Added Bundle test suite (47 tests):
    • DI configuration validation
    • CompilerPass with real ContainerBuilder
    • Generator context, templates, file generation
    • make:integration via CommandTester
  • Added engine-level tests:
    • Full 401 retry matrix
    • Dynamic prefix resolution
    • FakeClient with queued exceptions for realistic HTTP failure simulation
  • Removed Bundle from PHPUnit coverage exclusions
  • Final metrics:
    • 177 tests
    • 100% MSI (214/214 mutants killed)

📚 Documentation & Architecture

  • Added docs/class-graph.md with Mermaid diagrams:
    • Class relationships
    • Runtime execution flow
    • Exception hierarchy
  • Documentation aligned with current behavior:
    • Removed resolvePathCallback
    • Added 401 retry behavior explanation
    • Clarified headers & prefix configuration
  • Updated:
    • README
    • ARCHITECTURE
    • TESTING.md
    • CLAUDE.md
    • AI-AGENT-USAGE
  • Mutation testing guidelines simplified (no brittle counters)

⚙️ Tooling

  • PHPStan now runs with --memory-limit=1G in Makefile for improved analysis stability.

⚠️ Breaking Change

  • CachePort interface now requires:
delete(string $key): void
v1.27.0

Bug fixes

resolvePath was a required method on a public interface (ActionContextInterface) v1.26.0 added resolvePath(string $path): ?string as a mandatory method on ActionContextInterface. Any existing implementation would crash with a fatal error after composer update. The method has been removed from the base interface and moved to the new optional PathResolvableContextInterface.

defaultAuthHeaders() declared private in the trait prevented overriding The docblock stated the method could be overridden in consuming classes, but PHP's private-method binding silently ignored any class-level override. Visibility is now protected.

Constructor-injected Accept header was silently discarded In both SymfonyHttpClientAdapter and GraphQLClientAdapter, the array_merge order caused resolveHeaders() (which bundled defaultAuthHeaders()) to overwrite constructor-supplied headers. An adapter configured with $defaultHeaders = ['Accept' => 'application/xml'] would always send application/json. Priority order is now correct: defaultAuthHeaders < $defaultHeaders < auth headers < per-request headers.

getPath() returned an empty string without throwing If resolvePath() returned "", the null !== $resolved guard let it through and the HTTP adapter built a malformed URL. PathResolutionException::resolverReturnedEmptyPath() is now thrown instead.

resolveHeaders() mixed auth logic with default headers The ResolvesAuthHeaders trait always seeded the header array with $this->defaultAuthHeaders() before adding auth headers, coupling two responsibilities and making priority control impossible from outside. resolveHeaders() now returns only authentication headers (Bearer / Basic / ApiKey).

Unsafe offset access on mixed in GraphQLClientAdapter (phpstan level max) GraphQL error message extraction accessed $data['errors'][0]['message'] directly without type guards, failing phpstan at level max. Extraction is now type-safe with explicit is_array checks at each level.


New

PathResolvableContextInterface New optional interface (extends ActionContextInterface) for contexts that need full control over path resolution. Returning null from resolvePath() delegates to the default {placeholder} resolver.

class MyContext implements PathResolvableContextInterface { public function resolvePath(string $path): ?string { return '/v2' . $path; // return null to fall back to placeholder resolution } }

PathResolutionException::resolverReturnedEmptyPath() New exception factory thrown when resolvePath() returns "".


Migration from v1.26.0

If you implemented resolvePath() on your context class to customize path resolution, change implements ActionContextInterface to implements PathResolvableContextInterface — no other changes needed.

If you implemented resolvePath() only to satisfy the interface (returning null), remove the method entirely.


Internal

  • DefaultActionContext and FakeContext drop the resolvePath(): null stub that only existed to satisfy the old interface contract.
  • phpstan level max: 0 errors across src/Core and src/Infrastructure/Http.
  • CLAUDE.md and ARCHITECTURE.md added to the repository.
v1.26.0

feat: add context-driven path resolution support

  • Introduce resolvePath() method in ActionContextInterface to enable customizable path resolution logic.
  • Update DefaultActionContext and FakeContext to include resolvePath() with default implementations.
  • Refactor AbstractAction to rely on context's resolvePath() method for path resolution, falling back to default logic if null is returned.
  • Simplify exception handling by removing unused resolverDidNotReturnString case.
  • Update tests to validate context-driven path resolution behavior, including custom resolvers and fallback scenarios.
  • Optimize mutation testing execution by reducing test timing durations.
v1.25.4

Added

  • Mutation testing with Infection — full mutation test suite covering all production source files. Reached 94% Covered MSI (Mutation Score Indicator) with 100% code coverage. Zero mutations escaped in the core engine logic.
  • tests/Core/ExceptionMessagesTest.php — verifies every exception constructs the correct message. Kills MethodCallRemoval and Concat mutants across all seven exception classes.
  • tests/Infrastructure/GraphQLClientAdapterHeadersTest.php — covers all three auth types (bearer, basic, api_key), the default arm, the += merge semantics, the prefix ?? 'Bearer' coalesce, and the early return when no auth is present.
  • tests/Infrastructure/GraphQLClientAdapterTest.php — extended with statusCode assertions on body-validation and GraphQL error paths.
  • tests/Infrastructure/SymfonyHttpClientAdapterBodyTest.php — covers POST/PUT/PATCH body serialisation, GET without body, >= 400 boundary at exactly 400, \Throwable network error wrapping, RequestResponseException rethrow, 204 empty response, and whitespace-only body.
  • tests/Infrastructure/SymfonyHttpClientAdapterResolveHeadersTest.php — mirrors the GraphQL header tests for the REST adapter: basic, api_key, default, += preservation, LogicalAnd token guard, and the prefix ?? 'Bearer' coalesce.
  • infection.json5 — Infection configuration with minMsi: 90 and minCoveredMsi: 94.

Changed

  • infection.json5 thresholds raised to match current test quality: minMsi: 90, minCoveredMsi: 94.

Notes

The remaining 6% of escaped mutants are infrastructure-level false positives: getContent(throw: false) guards, \Throwable network error catch blocks, and a CastString on a scalar value that passes through a is_scalar() check two lines above. No production logic was modified — all improvements were test additions only.

v1.25.3

Fixed

  • config_path is now validated at container compile time via IntegrationConfigurationException instead of throwing a generic \InvalidArgumentException from the compiler pass.
  • YAML action field is validated at YamlConfigAdapter construction time, before the PHPDoc cast, producing a descriptive error on misconfigured files.

Removed

  • has() removed from CachePort interface and Psr6CacheAdapter — the method was not used by the engine and was a leftover from a previous refactor.
v1.25.2

fix: enforce config_path validation at compile time, update exception handling, and revise related test/documentation

v1.25.1

[v1.25.1]

Fixed

  • IntegrationRegistry — Integration names consisting only of whitespace (e.g. ' ') passed the empty-string guard and were registered silently under an unreachable key. The guard now uses trim().

Docs

  • DOCUMENTATION.md / DOCUMENTATION_ES.md — Added warning to the config_path section: the file is resolved at runtime, not at compile time. A missing path will not be caught by cache:warmup.
  • DOCUMENTATION.md / DOCUMENTATION_ES.md — Added limitation note to the dynamic auth section: the basic auth type is incompatible with the dynamic auth flow. Use a custom ClientInterface instead.

v1.25.0

[v1.25.0] "The Last Mile"

The details that only show up in production.

Fixed

  • IntegrationEngine — Dynamic auth cache keys were not scoped to the integration name. Two integrations with a token action of the same name (e.g. FetchToken) shared the same cache key integration_engine.token.FetchToken, causing one integration to authenticate silently with another's token. Keys are now scoped: integration_engine.token.{integrationName}.{actionName}.
  • IntegrationEngine$integrationName: string added to the constructor. The IntegrationCompilerPass passes the integration name at wiring time. IntegrationEngineTestCase updated accordingly.
  • IntegrationEngine::applyMapper() — Added explanatory comment clarifying why the MapperActionMismatchException guard is intentionally duplicated from AbstractMapper::map(): the engine-side guard is a fail-fast for misconfigured custom ConfigPort or ClientInterface implementations; the mapper-side guard is the public contract for callers outside the engine flow.

Added

  • DynamicAuthorizationConfig — New prefix field (default 'Bearer'). APIs using non-Bearer Authorization schemes (e.g. Authorization: Token abc123, Authorization: Digest ...) can now be configured without a custom ClientInterface. The prefix is forwarded through StaticAuthorizationConfig params and applied by both SymfonyHttpClientAdapter and GraphQLClientAdapter.

Docs

  • DOCUMENTATION.md / DOCUMENTATION_ES.md:
    • Static auth table: bearer row notes that the prefix is configurable via prefix:.
    • Dynamic auth YAML: prefix: added as a commented optional field with explanation.
    • "What happens at runtime" step 3: cache key updated to reflect the new {integrationName}.{actionName} format.
    • Path resolution section: documented the \w+ limitation for hyphenated placeholders (e.g. {user-id}) with a ready-to-use resolvePathCallback() example using {([^}]+)}.
    • Error reference table: $e->statusCode and $e->context corrected from the nonexistent getStatusCode() / getContext() getter methods.

Tests

  • DynamicAuthTest — 7 → 8 tests. Added dynamicAuthUsesCustomPrefixInAuthorizationHeader: verifies that prefix: 'Token' in DynamicAuthorizationConfig is carried through to StaticAuthorizationConfig params after token resolution.
  • DynamicAuthTest — Cache key assertions updated to the new integration_engine.token.test_integration.{action} format.
v1.24.0

[v1.24.0] "La Letra Pequeña"

Lo que nadie lee hasta que algo falla.

Fixed

  • SymfonyHttpClientAdapterDELETE requests no longer send a body. DELETE was listed alongside POST, PUT, and PATCH in the body serialisation check despite IntegrationContext::hasBody() explicitly excluding it. The adapter now matches the contract.
  • Configuration — The client: key validation was hardcoded to ['rest', 'graphql'], silently rejecting any custom adapter type registered via ClientAdapterInterface. Validation now only enforces non-empty — the compiler pass is the right place to catch unknown types.
  • Psr6CacheAdapter — Key sanitisation now includes . alongside the PSR-6 reserved characters. The engine generates keys like integration_engine.token.{action} — some pools (Redis, APCu) reject dots. The sanitiser no longer lies about being complete.
  • RequestResponseException — The HTTP status code was being passed as the second argument to RuntimeException::__construct(), polluting $e->getCode() with HTTP semantics. The exception already exposes $statusCode as a public property — getCode() now returns 0 as intended.

Removed

  • TemplateRenderer::body() — Dead method. Generated a ActionBodyInterface skeleton that was never called from IntegrationFileGenerator. Removed without ceremony.
  • IntegrationContext — Removed isGet(), isPost(), isPut(), isDelete(), isPatch(). Five helpers that existed because a language model thought they looked useful. hasBody() and hasResponse() inline the comparisons directly — no intermediate layer needed.

Docs

  • Body system (DOCUMENTATION.md, DOCUMENTATION_ES.md, agent-guide.md) — DELETE removed from the list of methods that send a body.
  • Cache default (DOCUMENTATION.md, DOCUMENTATION_ES.md, agent-guide.md) — Corrected from the nonexistent InMemoryCacheAdapter to Psr6CacheAdapter wrapping cache.app.
  • Adapter precedence (DOCUMENTATION.md, DOCUMENTATION_ES.md) — "always take precedence" softened to reflect that it depends on registration order, not a framework guarantee.
  • GraphQLBodyInterface docblock and agent guide — The file_get_contents example no longer implies built-in .graphql file support. Both inline and file-based patterns are shown as user-side choices.

Tests

  • Added Psr6CacheAdapterTest — 8 tests covering get/set/has and key sanitisation, including the dot regression (integration_engine.token.*).
  • Updated TESTING.md — Directory tree and suite documentation updated to reflect the full Infrastructure test suite: ClientAdapterResolverTest, GraphQLClientAdapterTest, Psr6CacheAdapterTest, SymfonyHttpClientAdapterHeadersTest.
v1.23.0

CHANGELOG — v1.23.0 "The Interpreter" Core

Added ClientAdapterInterface — extends ClientInterface with getClientType(), requiresPath() and requiresMethod(). The contract for autodiscoverable adapters Added GraphQLBodyInterface — extends ActionBodyInterface with getQuery() and getVariables(). Queries can be declared inline or loaded from .graphql files via file_get_contents()

Infrastructure

Added GraphQLClientAdapter — built-in GraphQL adapter. Always POST to the endpoint URL, serialises body as { query, variables }, extracts data[] before the mapper, detects GraphQL errors in HTTP 200 responses and throws RequestResponseException Added ClientAdapterResolver — registry of adapter classes keyed by getClientType(). Built after compile time from tagged services. Later registrations override earlier ones — project adapters always win over bundle built-ins SymfonyHttpClientAdapter now implements ClientAdapterInterface with requiresPath(): true and requiresMethod(): true

Bundle

Configuration — new client key per integration: "rest" (default), "graphql", or any registered custom type. client_service still overrides everything IntegrationCompilerPass — builds ClientAdapterResolver from integration_engine.client_adapter tagged services. Two guards before registration: class_exists() and is_a(ClientAdapterInterface). Fills the resolver service via addMethodCall so the command can use it at runtime MakeIntegrationCommand — action argument now optional. Client type chosen from registered adapters dynamically. Path and method questions driven by requiresPath() and requiresMethod() — no hardcoded type comparisons. client: soap appears in the list as soon as the adapter is registered IntegrationContext — replaced isGraphQL() with adapterRequiresPath and adapterRequiresMethod capabilities. Protocol-agnostic TemplateRenderer — YAML entry and action scaffold driven by adapter capabilities, not by type string

Tests

Added GraphQLClientAdapterTest — 15 tests covering body serialisation, endpoint URL, data extraction, GraphQL errors in 200, HTTP errors, invalid body, headers, auth, and ClientAdapterInterface capabilities Added ClientAdapterResolverTest — 7 tests covering registration, resolution, project-over-bundle override, full map, empty resolver, and custom adapter

Documentation

DOCUMENTATION.md / DOCUMENTATION_ES.md — Quick start GraphQL example, scaffolding table REST vs GraphQL, client: key in YAML config, GraphQLBodyInterface with file_get_contents pattern, extensibility section with full ClientAdapterInterface example including requiresPath and requiresMethod, two new error entries TESTING.md — GraphQLClientAdapterTest and ClientAdapterResolverTest suites documented integration-engine-agent-guide.md — ClientAdapterInterface (3.7) and GraphQLBodyInterface (3.8) sections added, client: vs client_service: FAQ entry

v1.22.0

Tests

Extracted shared engine setup into IntegrationEngineTestCase — DynamicAuthTest and EngineContractTest extend it, eliminating duplicated setUp() and property declarations Moved all reusable fakes to tests/Fake/ — FakeCache, FakeClient, FakeConfigPort, FakeContext, FakeTokenAction, FakeTokenMapper, FakeTokenResponse, FakePathAction, FakeProtectedAction Replaced all anonymous ActionContextInterface classes with FakeContext::create([...]) Replaced EngBasicResponse (inline duplicate of FakeTokenResponse) with FakeTokenResponse in EngineContractTest Updated AbstractActionTest to expect PathResolutionException instead of \RuntimeException — tests now protect the actual contract FakePathAction and FakeProtectedAction simplified to hasResponse: false — these actions are used to verify auth and context propagation, not response mapping

Documentation

Added TESTING.md — full guide to the test suite: philosophy, structure, per-test rationale, fakes inventory, and base class explanation README.md updated with reference to TESTING.md

Housekeeping

Removed TestingIdeas.md — superseded by TESTING.md Removed phpunit.xml.dist.bak — leftover from --migrate-configuration Removed empty directories src/Bundle/Generator/OpenApi/ and src/Bundle/Generator/Schema/ Added .DS_Store to .gitignore Removed tracked artefacts from repo: .DS_Store, .idea/, .php-cs-fixer.cache, .phpunit.result.cache

v1.20.0

Tests

Added EngineContractTest — engine contract suite: full flow, empty response, ActionNotFoundException, NotMappedActionException, MapperActionMismatchException, context propagation to client

Documentation

Restructured DOCUMENTATION.md — new section order from quick start to deep reference, Anti-Corruption Layer pattern documented explicitly, complete Action/Mapper/Response examples, dynamic auth end-to-end example Added DOCUMENTATION_ES.md — full Spanish translation Updated README.md — quick start first, correct usage pattern with facade and application service Updated integration-engine-agent-guide.md — aligned with current contracts, generic templates, removed incorrect hasBody() reference, added ACL pattern

Housekeeping

Removed InvalidMapperException — declared but never thrown Removed InvalidMethodException — declared but never thrown phpunit.xml.dist — added coverage exclusions for interfaces and ports, enabled testdox output .php-cs-fixer.dist.php — disabled php_unit_test_class_requires_covers to prevent deprecated #[CoversNothing] injection, added setUnsupportedPhpVersionAllowed(true)

v1.19.0

v1.19.0 — Stateless actions

What's new

Actions are now fully stateless

AbstractAction no longer stores context. Previously, context was injected into the action via withContext() and retrieved via getActionContext(), which meant the action carried execution state. This was conceptually wrong — an action is a contract that defines an operation, not a call. Execution state belongs to the engine.

The refactor removes three things from AbstractAction:

  • private ?ActionContextInterface $context
  • withContext(?ActionContextInterface): static
  • getActionContext(): ?ActionContextInterface

And changes getPath() to accept the context as a parameter:

// Before
$action->withContext($context)->getPath();

// After
$action->getPath($context);

The same action instance can now be called with different contexts in successive requests without cloning or mutation:

$action->getPath(DefaultActionContext::create(['id' => 1])); // /orders/1
$action->getPath(DefaultActionContext::create(['id' => 2])); // /orders/2

Context travels through the engine as a local variable

The engine passes context as an explicit parameter through send()applyAuthorization()client->send(). The action never sees its own context — it only resolves the path when the engine asks it to, with the context the engine provides at that moment.

This eliminates the regression risk from applyAuthorization() accidentally dropping the context when reconstructing the action with a resolved token.

ClientInterface and SymfonyHttpClientAdapter updated

send() now accepts ?ActionContextInterface $context as a second parameter:

public function send(
    AbstractAction $action,
    ?ActionContextInterface $context = null,
    ?RequestHeadersInterface $headers = null,
): array;

The adapter resolves the path at HTTP call time: $action->getPath($context).


Breaking changes

AbstractAction::withContext() removed. Any code calling this method must be updated to pass context directly to getPath() or to send().

AbstractAction::getActionContext() removed. Context is no longer accessible from outside the engine.

ClientInterface::send() signature changed. Custom ClientInterface implementations must add ?ActionContextInterface $context = null as the second parameter.


Upgrade notes

If you have custom ClientInterface implementations, update the send() signature:

public function send(
    AbstractAction $action,
    ?ActionContextInterface $context = null,  // ← add this
    ?RequestHeadersInterface $headers = null,
): array;

If you have custom resolvePathCallback() implementations that called withContext() internally, update them to use the context parameter passed directly to the callback:

protected function resolvePathCallback(): callable
{
    return function (string $path, ?ActionContextInterface $context): string {
        // $context is passed directly — no withContext() needed
        return $resolvedPath;
    };
}
v1.18.0

v1.18.0 — DefaultActionContext

What's new

DefaultActionContext — built-in context implementation

The bundle now ships with a general-purpose ActionContextInterface implementation that eliminates boilerplate for the common case:

use IntegrationEngine\Core\Contract\DefaultActionContext;

$registry->get('orders_api')->send(
    actionName: 'GetOrder',
    context: DefaultActionContext::create(['id' => 42]),
);

Previously, every project had to write its own context class even for simple key-value parameter passing. DefaultActionContext handles this without any custom code.

The contract stays uniform — DefaultActionContext implements ActionContextInterface — so existing custom context classes continue to work unchanged. DefaultActionContext is the default; custom classes are for when the context needs validation, type coercion, or domain logic.

// Before — required a custom class for every parameter
context: GetOrderContext::create(['id' => $id])

// After — built-in for simple cases
context: DefaultActionContext::create(['id' => $id])

assert() pattern for typed responses

The documentation now covers the recommended pattern for narrowing ResponseInterface to a concrete type without triggering PHPStan errors:

public function getOrder(int $id): GetOrderResponse
{
    $response = $this->engine->send(
        actionName: GetOrderAction::getName(),
        context: DefaultActionContext::create(['id' => $id]),
    );

    \assert($response instanceof GetOrderResponse);

    return $response;
}

assert() satisfies PHPStan's type narrowing, signals intent clearly, and has zero cost in production when assertions are disabled.


Upgrade notes

No breaking changes. DefaultActionContext is a new addition — nothing existing is affected.

Custom context classes continue to work as before. Migrating them to DefaultActionContext is optional and only makes sense when the class has no logic beyond key-value storage.

v1.17.0

v1.17.0 — Mesa because is stable

Una mesa no se cae.

This release focuses on developer experience: the scaffolding command now works out of the box with no prior setup, and the documentation covers the full surface of the bundle.


What's new

Scaffolding — zero config required

make:integration no longer requires a pre-existing config/packages/integration_engine.yaml. On first run the command asks for the integration base URL and creates the config file automatically.

The command now also asks for the action path and HTTP method interactively, so the generated YAML entry is complete and correct from the start:

v.1.16.2
v1.16.1

fix instalation issues

v1.16.0
v1.15.1

chore: update composer.json to define Symfony version requirement and register IntegrationEngineBundle

v1.14.0
v1.13.0
v1.12.0
v1.11.0

Resolves a set of misconfiguration issues in the Symfony bundle layer that prevented integrations from being registered in the container. The core engine was not affected. All fixes are isolated to the bundle's dependency injection setup and the code generator.

v1.10.0

Changelog All notable changes to carlosgude/integration-engine will be documented in this file. The format is based on Keep a Changelog.

[Unreleased] Fixed

IntegrationEngineBundle — the IntegrationCompilerPass was never registered because IntegrationEngineBundle did not override build(). Integrations configured under integration_engine.integrations were silently ignored and any call to IntegrationRegistry::get() threw IntegrationNotFoundException. IntegrationCompilerPass — base_url was read from configuration but never passed to the HTTP client. Each integration's SymfonyHttpClientAdapter is now constructed with its own base_url, one service definition per integration (integration_engine.http_client.{name}). The phantom alias integration_engine.http_client.default has been removed. IntegrationEngine::send() — removed the $yamlUrl argument. The config path is now injected into YamlConfigAdapter at compile time via the CompilerPass; there is no reason to pass it again at runtime. Call sites are simplified to ->send('ActionName') or ->send('ActionName', $body). YamlConfigAdapter — config loading moved from setYaml() (called on every send()) to the constructor (called once at container compile time). The setYaml() method and its corresponding ConfigPort interface method have been removed. services.yaml — added explicit integration_engine.cache.default service definition pointing to InMemoryCacheAdapter. Added exclude entries for YamlConfigAdapter, SymfonyHttpClientAdapter, and IntegrationEngine to prevent Symfony's autowire scan from trying to resolve their dynamic constructor arguments automatically. MakeIntegrationCommand — the integration namespace was built by concatenating $name onto $baseNamespace before passing it to IntegrationContext, causing every generated namespace to include the integration name twice (e.g. App\Integrations\RickAndMorty\RickAndMorty...). The command now passes $baseNamespace as-is; IntegrationContext::integrationNamespace() appends $name internally. IntegrationFileGenerator — generateIntegrationFiles() included the YAML config file in its output, while appendActionToConfig() unconditionally appended the same entry afterward. The first action was always written twice. The YAML file is now managed exclusively by appendActionToConfig(). TemplateRenderer::yamlEntry() — heredoc indentation caused leading whitespace to be included in the generated YAML, producing structurally invalid files. The method now builds the entry as a plain interpolated string.

Changed

IntegrationRegistry is now the only entry point for consumers. The IntegrationEngine instance is no longer intended to be injected directly into application services or controllers. ConfigPort interface no longer declares setYaml().

v1.9.0
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
boundwize/jsonrecast
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata