Skip to content

Commit 6e53388

Browse files
authored
Exclude namespaces for embedded schemas if they are the same as root schema namespace (#5)
* Exclude namespaces for embedded schemas if they are the same as root * Added command option for excluding namespaces
1 parent 1fce481 commit 6e53388

File tree

4 files changed

+158
-17
lines changed

4 files changed

+158
-17
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ $merger->merge();
4242

4343
### Generating schemas from classes
4444
Please note, that this feature is highly experimental.
45-
You probably still need to adjust the generated templates, but it gives you a basic tempalte to work with.
46-
Class direcotries: Directories containing the classes you want to generate schemas from
45+
You probably still need to adjust the generated templates, but it gives you a basic template to work with.
46+
Class directories: Directories containing the classes you want to generate schemas from
4747
Output directory: output directory for your generated schema templates
4848

4949
#### Generate schemas (code)

src/Command/SubSchemaMergeCommand.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ protected function configure(): void
2929
null,
3030
InputOption::VALUE_NONE,
3131
'Use template filename as schema filename'
32+
)
33+
->addOption(
34+
'optimizeSubSchemaNamespaces',
35+
null,
36+
InputOption::VALUE_NONE,
37+
'Exclude namespaces from embedded entities if they are different than root schema namespace'
3238
);
3339
}
3440

@@ -52,10 +58,10 @@ public function execute(InputInterface $input, OutputInterface $output): int
5258

5359
$result = $merger->merge(
5460
(bool) $input->getOption('prefixWithNamespace'),
55-
(bool) $input->getOption('useFilenameAsSchemaName')
61+
(bool) $input->getOption('useFilenameAsSchemaName'),
62+
(bool) $input->getOption('optimizeSubSchemaNamespaces')
5663
);
5764

58-
5965
// retrieve the argument value using getArgument()
6066
$output->writeln(sprintf('Merged %d root schema files', $result));
6167

src/Merger/SchemaMerger.php

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@
77
use AvroSchemaParseException;
88
use PhpKafka\PhpAvroSchemaGenerator\Avro\Avro;
99
use PhpKafka\PhpAvroSchemaGenerator\Exception\SchemaMergerException;
10-
use PhpKafka\PhpAvroSchemaGenerator\Exception\SchemaGenerationException;
11-
use PhpKafka\PhpAvroSchemaGenerator\Exception\UnknownSchemaTypeException;
1210
use PhpKafka\PhpAvroSchemaGenerator\Registry\SchemaRegistryInterface;
1311
use PhpKafka\PhpAvroSchemaGenerator\Schema\SchemaTemplateInterface;
1412

1513
final class SchemaMerger implements SchemaMergerInterface
1614
{
17-
1815
/**
1916
* @var string
2017
*/
@@ -49,12 +46,15 @@ public function getOutputDirectory(): string
4946

5047
/**
5148
* @param SchemaTemplateInterface $schemaTemplate
49+
* @param bool $optimizeSubSchemaNamespaces
5250
* @return SchemaTemplateInterface
5351
* @throws AvroSchemaParseException
5452
* @throws SchemaMergerException
5553
*/
56-
public function getResolvedSchemaTemplate(SchemaTemplateInterface $schemaTemplate): SchemaTemplateInterface
57-
{
54+
public function getResolvedSchemaTemplate(
55+
SchemaTemplateInterface $schemaTemplate,
56+
bool $optimizeSubSchemaNamespaces = false
57+
): SchemaTemplateInterface {
5858
$definition = $schemaTemplate->getSchemaDefinition();
5959

6060
do {
@@ -78,7 +78,8 @@ public function getResolvedSchemaTemplate(SchemaTemplateInterface $schemaTemplat
7878
$definition = $this->replaceSchemaIdWithDefinition(
7979
$definition,
8080
$schemaId,
81-
$embeddedTemplate->getSchemaDefinition()
81+
$embeddedTemplate->getSchemaDefinition(),
82+
$optimizeSubSchemaNamespaces
8283
);
8384
}
8485
} while (true === $exceptionThrown);
@@ -94,32 +95,40 @@ private function getSchemaIdFromExceptionMessage(string $exceptionMessage): stri
9495
private function replaceSchemaIdWithDefinition(
9596
string $definition,
9697
string $schemaId,
97-
string $embeddedDefinition
98+
string $embeddedDefinition,
99+
bool $optimizeSubSchemaNamespaces = false
98100
): string {
99101
$idString = '"' . $schemaId . '"';
100102

103+
if (true === $optimizeSubSchemaNamespaces) {
104+
$embeddedDefinition = $this->excludeNamespacesForEmbeddedSchema($definition, $embeddedDefinition);
105+
}
106+
101107
$pos = strpos($definition, $idString);
102108

103109
return substr_replace($definition, $embeddedDefinition, $pos, strlen($idString));
104110
}
105111

106-
107112
/**
108-
* @param boolean $prefixWithNamespace
109-
* @param boolean $useTemplateName
113+
* @param bool $prefixWithNamespace
114+
* @param bool $useTemplateName
115+
* @param bool $optimizeSubSchemaNamespaces
110116
* @return integer
111117
* @throws AvroSchemaParseException
112118
* @throws SchemaMergerException
113119
*/
114-
public function merge(bool $prefixWithNamespace = false, bool $useTemplateName = false): int
115-
{
120+
public function merge(
121+
bool $prefixWithNamespace = false,
122+
bool $useTemplateName = false,
123+
bool $optimizeSubSchemaNamespaces = false
124+
): int {
116125
$mergedFiles = 0;
117126
$registry = $this->getSchemaRegistry();
118127

119128
/** @var SchemaTemplateInterface $schemaTemplate */
120129
foreach ($registry->getRootSchemas() as $schemaTemplate) {
121130
try {
122-
$resolvedTemplate = $this->getResolvedSchemaTemplate($schemaTemplate);
131+
$resolvedTemplate = $this->getResolvedSchemaTemplate($schemaTemplate, $optimizeSubSchemaNamespaces);
123132
} catch (SchemaMergerException $e) {
124133
throw $e;
125134
}
@@ -176,4 +185,26 @@ public function transformExportSchemaDefinition(array $schemaDefinition): array
176185

177186
return $schemaDefinition;
178187
}
188+
189+
/**
190+
* @param string $definition
191+
* @param string $embeddedDefinition
192+
* @return string
193+
*/
194+
private function excludeNamespacesForEmbeddedSchema(string $definition, string $embeddedDefinition): string
195+
{
196+
$decodedRootDefinition = json_decode($definition, true);
197+
$decodedEmbeddedDefinition = json_decode($embeddedDefinition, true);
198+
199+
if (
200+
isset($decodedRootDefinition['namespace']) && isset($decodedEmbeddedDefinition['namespace']) &&
201+
$decodedRootDefinition['namespace'] === $decodedEmbeddedDefinition['namespace']
202+
) {
203+
unset($decodedEmbeddedDefinition['namespace']);
204+
/** @var string $embeddedDefinition */
205+
$embeddedDefinition = json_encode($decodedEmbeddedDefinition);
206+
}
207+
208+
return $embeddedDefinition;
209+
}
179210
}

tests/Unit/Merger/SchemaMergerTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,110 @@ public function testGetResolvedSchemaTemplate()
116116
->with($expectedResult)
117117
->willReturn($rootSchemaTemplate);
118118

119+
$merger = new SchemaMerger($schemaRegistry);
120+
121+
$merger->getResolvedSchemaTemplate($rootSchemaTemplate);
122+
}
123+
124+
public function testGetResolvedSchemaTemplateWithOptimizedSubSchemaNamespaces()
125+
{
126+
$rootDefinition = '{
127+
"type": "record",
128+
"namespace": "com.example",
129+
"name": "Book",
130+
"fields": [
131+
{ "name": "items", "type": {"type": "array", "items": "com.example.Page" }, "default": [] }
132+
]
133+
}';
134+
$subschemaDefinition = '{
135+
"type": "record",
136+
"namespace": "com.example",
137+
"name": "Page",
138+
"fields": [
139+
{ "name": "number", "type": "int" }
140+
]
141+
}';
142+
143+
$expectedResult = '{
144+
"type": "record",
145+
"namespace": "com.example",
146+
"name": "Book",
147+
"fields": [
148+
{ "name": "items", "type": {"type": "array", "items": {"type":"record","name":"Page","fields":[{"name":"number","type":"int"}]} }, "default": [] }
149+
]
150+
}';
151+
152+
$subschemaTemplate = $this->getMockForAbstractClass(SchemaTemplateInterface::class);
153+
$subschemaTemplate
154+
->expects(self::once())
155+
->method('getSchemaDefinition')
156+
->willReturn($subschemaDefinition);
157+
$schemaRegistry = $this->getMockForAbstractClass(SchemaRegistryInterface::class);
158+
$schemaRegistry
159+
->expects(self::once())
160+
->method('getSchemaById')
161+
->with('com.example.Page')
162+
->willReturn($subschemaTemplate);
163+
$rootSchemaTemplate = $this->getMockForAbstractClass(SchemaTemplateInterface::class);
164+
$rootSchemaTemplate
165+
->expects(self::once())
166+
->method('getSchemaDefinition')
167+
->willReturn($rootDefinition);
168+
$rootSchemaTemplate
169+
->expects(self::once())
170+
->method('withSchemaDefinition')
171+
->with($expectedResult)
172+
->willReturn($rootSchemaTemplate);
173+
174+
$merger = new SchemaMerger($schemaRegistry);
175+
176+
$merger->getResolvedSchemaTemplate($rootSchemaTemplate, true);
177+
}
178+
179+
public function testGetResolvedSchemaTemplateWithDifferentNamespaceForEmbeddedSchema()
180+
{
181+
$rootDefinition = '{
182+
"type": "record",
183+
"namespace": "com.example",
184+
"name": "Book",
185+
"fields": [
186+
{ "name": "items", "type": {"type": "array", "items": "com.example.other.Page" }, "default": [] }
187+
]
188+
}';
189+
$subschemaDefinition = '{
190+
"type": "record",
191+
"namespace": "com.example.other",
192+
"name": "Page",
193+
"fields": [
194+
{ "name": "number", "type": "int" }
195+
]
196+
}';
197+
198+
$expectedResult = str_replace('"com.example.other.Page"', $subschemaDefinition, $rootDefinition);
199+
200+
$subschemaTemplate = $this->getMockForAbstractClass(SchemaTemplateInterface::class);
201+
$subschemaTemplate
202+
->expects(self::once())
203+
->method('getSchemaDefinition')
204+
->willReturn($subschemaDefinition);
205+
206+
$schemaRegistry = $this->getMockForAbstractClass(SchemaRegistryInterface::class);
207+
$schemaRegistry
208+
->expects(self::once())
209+
->method('getSchemaById')
210+
->with('com.example.other.Page')
211+
->willReturn($subschemaTemplate);
212+
213+
$rootSchemaTemplate = $this->getMockForAbstractClass(SchemaTemplateInterface::class);
214+
$rootSchemaTemplate
215+
->expects(self::once())
216+
->method('getSchemaDefinition')
217+
->willReturn($rootDefinition);
218+
$rootSchemaTemplate
219+
->expects(self::once())
220+
->method('withSchemaDefinition')
221+
->with($expectedResult)
222+
->willReturn($rootSchemaTemplate);
119223

120224
$merger = new SchemaMerger($schemaRegistry);
121225

0 commit comments

Comments
 (0)