Skip to content

Commit e1607ae

Browse files
Merge pull request #9 from MacPaw/feat/pass-all-baggage
feat: reworked to work with full baggage instead of only scheme
2 parents 8c77c30 + 8247bd3 commit e1607ae

16 files changed

Lines changed: 564 additions & 231 deletions

README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ The **SchemaContextBundle** provides a lightweight way to manage dynamic schema
77
## Features
88

99
- Extracts tenant schema param from baggage request header.
10-
- Stores schema context in a global `SchemaResolver`.
11-
- Injects schema info into Messenger messages via a middleware.
12-
- Rehydrates schema on message consumption via a middleware.
13-
- Provide decorator for Http clients to propagate schema header
10+
- Stores schema and baggage context in a global `BaggageSchemaResolver`.
11+
- Injects schema and baggage info into Messenger messages via a middleware.
12+
- Rehydrates schema and baggage on message consumption via a middleware.
13+
- Provide decorator for Http clients to propagate baggage header
1414

1515
---
1616

@@ -49,34 +49,35 @@ APP_NAME=develop
4949
## Usage
5050

5151
```php
52-
use Macpaw\SchemaContextBundle\Service\SchemaResolver;
52+
use Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver;
5353

54-
public function index(SchemaResolver $schemaResolver)
54+
public function index(BaggageSchemaResolver $schemaResolver)
5555
{
5656
$schema = $schemaResolver->getSchema();
57+
$baggage = $schemaResolver->getBaggage();
5758
// Use schema in logic
5859
}
5960
```
6061

61-
## Schema-Aware HTTP Client
62+
## Baggage-Aware HTTP Client
6263
Decorate your http client in your service configuration:
6364
```yaml
6465
services:
65-
schema_aware_payment_http_client:
66-
class: Macpaw\SchemaContextBundle\HttpClient\SchemaAwareHttpClient
66+
baggage_aware_payment_http_client:
67+
class: Macpaw\SchemaContextBundle\HttpClient\BaggageAwareHttpClient
6768
decorates: payment_http_client #http client to decorate
6869
arguments:
69-
- '@schema_aware_payment_http_client.inner'
70-
- '@Macpaw\SchemaContextBundle\Service\SchemaResolver'
71-
- '%schema_context.header_name%'
70+
- '@baggage_aware_payment_http_client.inner'
71+
- '@Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver'
72+
- '@Macpaw\SchemaContextBundle\Service\BaggageCodec'
7273
```
7374
7475
## Messenger Integration
7576
The bundle provides a middleware that automatically:
7677
77-
* Adds a SchemaStamp to dispatched messages
78+
* Adds a BaggageSchemaStamp to dispatched messages
7879
79-
* Restores the schema context on message handling
80+
* Restores the schema and baggage context on message handling
8081
8182
Enable the middleware in your `messenger.yaml`:
8283

@@ -86,7 +87,7 @@ framework:
8687
buses:
8788
messenger.bus.default:
8889
middleware:
89-
- Macpaw\SchemaContextBundle\Messenger\Middleware\SchemaMiddleware
90+
- Macpaw\SchemaContextBundle\Messenger\Middleware\BaggageMiddleware
9091
```
9192

9293
## Testing
@@ -100,4 +101,3 @@ Feel free to open issues and submit pull requests.
100101

101102
## License
102103
This bundle is released under the MIT license.
103-

config/services.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ services:
44
autoconfigure: true
55
public: false
66

7-
Macpaw\SchemaContextBundle\Service\SchemaResolver:
7+
Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver:
88
public: true
99
shared: true
1010

11-
Macpaw\SchemaContextBundle\EventListener\SchemaRequestListener:
11+
Macpaw\SchemaContextBundle\EventListener\BaggageRequestListener:
1212
arguments:
13-
$schemaResolver: '@Macpaw\SchemaContextBundle\Service\SchemaResolver'
13+
$baggageSchemaResolver: '@Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver'
1414
$schemaRequestHeader: '%schema_context.header_name%'
1515
$defaultSchema: '%schema_context.default_schema%'
1616
$appName: '%schema_context.app_name%'
1717
$allowedAppNames: '%schema_context.allowed_app_names%'
1818
tags:
1919
- { name: kernel.event_subscriber }
2020

21-
Macpaw\SchemaContextBundle\Messenger\Middleware\SchemaMiddleware:
21+
Macpaw\SchemaContextBundle\Messenger\Middleware\BaggageMiddleware:
2222
tags:
2323
- { name: messenger.middleware }

src/EventListener/SchemaRequestListener.php renamed to src/EventListener/BaggageRequestListener.php

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44

55
namespace Macpaw\SchemaContextBundle\EventListener;
66

7-
use Macpaw\SchemaContextBundle\Service\SchemaResolver;
7+
use Macpaw\SchemaContextBundle\Service\BaggageCodec;
8+
use Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver;
89
use Symfony\Component\HttpKernel\Event\RequestEvent;
910
use Symfony\Component\HttpKernel\KernelEvents;
1011
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1112

12-
class SchemaRequestListener implements EventSubscriberInterface
13+
class BaggageRequestListener implements EventSubscriberInterface
1314
{
1415
public function __construct(
15-
private SchemaResolver $schemaResolver,
16+
private BaggageSchemaResolver $baggageSchemaResolver,
17+
private BaggageCodec $baggageCodec,
1618
private string $schemaRequestHeader,
1719
private string $defaultSchema,
1820
private string $appName,
@@ -35,24 +37,18 @@ public function onKernelRequest(RequestEvent $event): void
3537
$request = $event->getRequest();
3638
$baggage = $request->headers->get('baggage');
3739

40+
$schema = null;
3841
if ($baggage) {
39-
foreach (explode(',', $baggage) as $part) {
40-
[$key, $value] = array_map(
41-
static fn(?string $v): ?string => $v !== null ? trim($v) : null,
42-
explode('=', $part, 2) + [null, null]
43-
);
44-
45-
if ($key === $this->schemaRequestHeader && $value !== null) {
46-
$schema = $value;
47-
break;
48-
}
49-
}
50-
}
42+
$baggage = $this->baggageCodec->decode($baggage);
43+
$this->baggageSchemaResolver->setBaggage($baggage);
5144

52-
$schema ??= $this->defaultSchema;
45+
$schema = $baggage[$this->schemaRequestHeader] ?? null;
46+
}
5347

5448
if ($schema !== null && $schema !== '') {
55-
$this->schemaResolver->setSchema($schema);
49+
$this->baggageSchemaResolver->setSchema($schema);
50+
} else {
51+
$this->baggageSchemaResolver->setSchema($this->defaultSchema);
5652
}
5753
}
5854

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44

55
namespace Macpaw\SchemaContextBundle\HttpClient;
66

7+
use Macpaw\SchemaContextBundle\Service\BaggageCodec;
78
use Symfony\Contracts\HttpClient\HttpClientInterface;
89
use Symfony\Contracts\HttpClient\ResponseInterface;
9-
use Macpaw\SchemaContextBundle\Service\SchemaResolver;
10+
use Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver;
1011
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
1112

12-
class SchemaAwareHttpClient implements HttpClientInterface
13+
class BaggageAwareHttpClient implements HttpClientInterface
1314
{
1415
public function __construct(
1516
private HttpClientInterface $inner,
16-
private SchemaResolver $schemaResolver,
17-
private string $schemaRequestHeader
17+
private BaggageSchemaResolver $baggageSchemaResolver,
18+
private BaggageCodec $baggageCodec,
1819
) {
1920
}
2021

@@ -23,18 +24,20 @@ public function __construct(
2324
*/
2425
public function request(string $method, string $url, array $options = []): ResponseInterface
2526
{
26-
$schema = $this->schemaResolver->getSchema();
27-
$baggageHeader = $this->schemaRequestHeader . '=' . $schema;
2827
$headers = isset($options['headers']) && is_array($options['headers'])
2928
? $options['headers']
3029
: [];
3130

32-
if (isset($headers['baggage'])) {
33-
$headers['baggage'] .= ',' . $baggageHeader;
34-
} else {
35-
$headers['baggage'] = $baggageHeader;
36-
}
31+
$baggage = isset($headers['baggage'])
32+
? $this->baggageCodec->decode($headers['baggage'])
33+
: [];
34+
35+
$baggage = [
36+
...$baggage,
37+
...($this->baggageSchemaResolver->getBaggage() ?? [])
38+
];
3739

40+
$headers['baggage'] = $this->baggageCodec->encode($baggage);
3841
$options['headers'] = $headers;
3942

4043
return $this->inner->request($method, $url, $options);
@@ -53,6 +56,6 @@ public function withOptions(array $options): static
5356
$wrapped = $this->inner->withOptions($options);
5457

5558
/** @phpstan-ignore-next-line */
56-
return new self($wrapped, $this->schemaResolver, $this->schemaRequestHeader);
59+
return new self($wrapped, $this->baggageSchemaResolver, $this->baggageCodec);
5760
}
5861
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Macpaw\SchemaContextBundle\Messenger\Middleware;
6+
7+
use Macpaw\SchemaContextBundle\Messenger\Stamp\BaggageSchemaStamp;
8+
use Macpaw\SchemaContextBundle\Service\BaggageCodec;
9+
use Macpaw\SchemaContextBundle\Service\BaggageSchemaResolver;
10+
use Symfony\Component\Messenger\Envelope;
11+
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
12+
use Symfony\Component\Messenger\Middleware\StackInterface;
13+
14+
class BaggageSchemaMiddleware implements MiddlewareInterface
15+
{
16+
public function __construct(
17+
private BaggageSchemaResolver $baggageSchemaResolver,
18+
private BaggageCodec $baggageCodec
19+
) {
20+
}
21+
22+
public function handle(Envelope $envelope, StackInterface $stack): Envelope
23+
{
24+
$stamp = $envelope->last(BaggageSchemaStamp::class);
25+
26+
if ($stamp instanceof BaggageSchemaStamp) {
27+
$this->baggageSchemaResolver
28+
->setSchema($stamp->schema)
29+
->setBaggage($this->baggageCodec->decode($stamp->baggage));
30+
31+
return $stack->next()->handle($envelope, $stack);
32+
}
33+
34+
$schema = $this->baggageSchemaResolver->getSchema();
35+
$baggage = $this->baggageCodec->encode($this->baggageSchemaResolver->getBaggage() ?? []);
36+
37+
if ($schema !== null && $schema !== '') {
38+
$envelope = $envelope->with(new BaggageSchemaStamp($schema, $baggage));
39+
}
40+
41+
return $stack->next()->handle($envelope, $stack);
42+
}
43+
}

src/Messenger/Middleware/SchemaMiddleware.php

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/Messenger/Stamp/SchemaStamp.php renamed to src/Messenger/Stamp/BaggageSchemaStamp.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
use Symfony\Component\Messenger\Stamp\StampInterface;
88

9-
class SchemaStamp implements StampInterface
9+
class BaggageSchemaStamp implements StampInterface
1010
{
11-
public function __construct(public string $schema)
11+
public function __construct(public string $schema, public string $baggage)
1212
{
1313
}
1414
}

src/Service/BaggageCodec.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Macpaw\SchemaContextBundle\Service;
6+
7+
class BaggageCodec
8+
{
9+
/**
10+
* @param array<string,string|null> $baggage
11+
*/
12+
public function encode(array $baggage): string
13+
{
14+
$parts = [];
15+
16+
foreach ($baggage as $key => $value) {
17+
if ($value === null) {
18+
$parts[] = trim($key);
19+
} else {
20+
$parts[] = trim($key) . '=' . trim($value);
21+
}
22+
}
23+
24+
return implode(',', $parts);
25+
}
26+
27+
/**
28+
* @return array<string,string|null>
29+
*/
30+
public function decode(string $baggage): array
31+
{
32+
$result = [];
33+
foreach (explode(',', $baggage) as $part) {
34+
$part = trim($part);
35+
if ($part === '') {
36+
continue;
37+
}
38+
39+
if (str_contains($part, '=')) {
40+
[$key, $value] = explode('=', $part, 2);
41+
$result[trim($key)] = trim($value);
42+
} else {
43+
$result[$part] = null;
44+
}
45+
}
46+
47+
return $result;
48+
}
49+
}

0 commit comments

Comments
 (0)