Skip to content

Commit bdb0b95

Browse files
authored
Merge pull request #116 from DoclerLabs/add-include-tags-and-exclude-tags
add include and exclude tags
2 parents 5033df3 + 345d8d0 commit bdb0b95

10 files changed

+161
-9
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [10.9.0] - 2024.08.16
8+
### Added
9+
- `INCLUDE_TAGS` and `EXCLUDE_TAGS` to whitelist/blacklist client generation based on operation tags
10+
711
## [10.8.1] - 2024.06.04
812
### Fixed
913
- Enums for path parameters

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ API client generator is a console application capable of auto-generating a [PSR1
3131
- It is base client independent, you are free to choose any [existing PSR-18 compliant client](https://packagist.org/providers/psr/http-client-implementation). Just choose the one which you already use, so generated client would not cause any conflicts with your dependencies. Although not recommended, you can also use or build your own PSR-18 implementation, as the generated client depends on PSR interfaces only.
3232
- Applies code style rules to generated code, you can specify your own.
3333
- Generates README and composer.json files with possibility to use your own template.
34-
- Supports `allOf` OpenAPI parameter.
34+
- Supports `allOf`, `oneOf`, `anyOf` OpenAPI parameters.
3535
- Supports nullable optional scheme property.
3636

3737
## Example
@@ -68,8 +68,8 @@ OPENAPI={path-to-specification}/openapi.yaml NAMESPACE=Group\SomeApiClient PACKA
6868
## Configuration
6969
The following environment variables are available:
7070

71-
| Variable | Required | Default | Enum | Example |
72-
|------------|---------|------------------|---------|---------------------------|
71+
| Variable | Required | Default | Enum | Example | Description |
72+
|------------|---------|------------------|---------|---------------------------|----------------------------|
7373
| `NAMESPACE` | yes | | | Group\\SomeApiClient |
7474
| `PACKAGE` | yes | | | group/some-api-client |
7575
| `OPENAPI ` | yes | | | /api/openapi.yaml |
@@ -81,6 +81,8 @@ The following environment variables are available:
8181
| `README_MD_TEMPLATE_DIR` | no | {path-to-repository}/template/README.md.twig | | /path/README.md.twig |
8282
| `HTTP_MESSAGE` | no | guzzle | guzzle, nyholm | nyholm |
8383
| `CONTAINER` | no | pimple | pimple | pimple |
84+
| `INCLUDE_TAGS` | no | | | tag1,tag2,tag3 | tag whitelist to select generated operations |
85+
| `EXCLUDE_TAGS` | no | | | tag1,tag2,tag3 | tag blacklist to select generated operations |
8486

8587
## Running tests
8688

src/Generator/ClientGenerator.php

+25
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
namespace DoclerLabs\ApiClientGenerator\Generator;
66

7+
use DoclerLabs\ApiClientGenerator\Ast\Builder\CodeBuilder;
78
use DoclerLabs\ApiClientGenerator\Ast\Builder\MethodBuilder;
89
use DoclerLabs\ApiClientGenerator\Ast\Builder\ParameterBuilder;
910
use DoclerLabs\ApiClientGenerator\Ast\ParameterNode;
11+
use DoclerLabs\ApiClientGenerator\Ast\PhpVersion;
1012
use DoclerLabs\ApiClientGenerator\Entity\Field;
1113
use DoclerLabs\ApiClientGenerator\Entity\Operation;
1214
use DoclerLabs\ApiClientGenerator\Entity\Response;
@@ -33,6 +35,16 @@
3335

3436
class ClientGenerator extends GeneratorAbstract
3537
{
38+
public function __construct(
39+
string $baseNamespace,
40+
CodeBuilder $builder,
41+
PhpVersion $phpVersion,
42+
private array $includeTags,
43+
private array $excludeTags
44+
) {
45+
parent::__construct($baseNamespace, $builder, $phpVersion);
46+
}
47+
3648
public function generate(Specification $specification, PhpFileCollection $fileRegistry): void
3749
{
3850
$classBuilder = $this->builder
@@ -42,6 +54,19 @@ public function generate(Specification $specification, PhpFileCollection $fileRe
4254
->addStmt($this->generateSendRequestMethod());
4355

4456
foreach ($specification->getOperations() as $operation) {
57+
if (
58+
(
59+
!empty($this->includeTags)
60+
&& empty(array_intersect($operation->tags, $this->includeTags))
61+
)
62+
|| (
63+
!empty($this->excludeTags)
64+
&& !empty(array_intersect($operation->tags, $this->excludeTags))
65+
)
66+
) {
67+
continue;
68+
}
69+
4570
$classBuilder->addStmt($this->generateAction($operation));
4671
}
4772

src/Input/Configuration.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class Configuration
2727

2828
public const STATIC_PHP_FILE_DIRECTORY = __DIR__ . '/../Output/Copy';
2929

30+
/**
31+
* @param string[] $includeTags
32+
* @param string[] $excludeTags
33+
*/
3034
public function __construct(
3135
public readonly string $specificationFilePath,
3236
public readonly string $baseNamespace,
@@ -39,7 +43,9 @@ public function __construct(
3943
public readonly string $composerJsonTemplateDir,
4044
public readonly string $readmeMdTemplateDir,
4145
public readonly string $httpMessage,
42-
public readonly string $container
46+
public readonly string $container,
47+
public readonly array $includeTags,
48+
public readonly array $excludeTags
4349
) {
4450
Assert::notEmpty($specificationFilePath, 'Specification file path is not provided.');
4551
Assert::notEmpty($baseNamespace, 'Namespace for generated code is not provided.');

src/ServiceProvider.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public function register(Container $pimple): void
9494
getenv('README_MD_TEMPLATE_DIR') ?: Configuration::DEFAULT_TEMPLATE_DIRECTORY,
9595
getenv('HTTP_MESSAGE') ?: Configuration::DEFAULT_HTTP_MESSAGE,
9696
getenv('CONTAINER') ?: Configuration::DEFAULT_CONTAINER,
97+
array_filter(explode(',', (string)getenv('INCLUDE_TAGS'))),
98+
array_filter(explode(',', (string)getenv('EXCLUDE_TAGS')))
9799
);
98100

99101
$pimple[GenerateCommand::class] = static fn (Container $container) => new GenerateCommand(
@@ -204,7 +206,9 @@ public function register(Container $pimple): void
204206
$pimple[ClientGenerator::class] = static fn (Container $container) => new ClientGenerator(
205207
$container[Configuration::class]->baseNamespace,
206208
$container[CodeBuilder::class],
207-
$container[PhpVersion::class]
209+
$container[PhpVersion::class],
210+
$container[Configuration::class]->includeTags,
211+
$container[Configuration::class]->excludeTags
208212
);
209213

210214
$pimple[SchemaGenerator::class] = static fn (Container $container) => new SchemaGenerator(

test/suite/functional/ConfigurationBuilder.php

+23-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class ConfigurationBuilder
3232

3333
private string $container;
3434

35+
private array $includeTags;
36+
37+
private array $excludeTags;
38+
3539
private function __construct()
3640
{
3741
$this->specificationFilePath = '/dir/path/openapi.yaml';
@@ -46,6 +50,8 @@ private function __construct()
4650
$this->readmeMdTemplateDir = Configuration::DEFAULT_TEMPLATE_DIRECTORY;
4751
$this->httpMessage = Configuration::DEFAULT_HTTP_MESSAGE;
4852
$this->container = Configuration::DEFAULT_CONTAINER;
53+
$this->includeTags = [];
54+
$this->excludeTags = [];
4955
}
5056

5157
public static function fake(): self
@@ -130,6 +136,20 @@ public function withContainer(string $container): self
130136
return $this;
131137
}
132138

139+
public function withIncludeTags(array $includeTags): self
140+
{
141+
$this->includeTags = $includeTags;
142+
143+
return $this;
144+
}
145+
146+
public function withExcludeTags(array $excludeTags): self
147+
{
148+
$this->excludeTags = $excludeTags;
149+
150+
return $this;
151+
}
152+
133153
public function build(): Configuration
134154
{
135155
return new Configuration(
@@ -144,7 +164,9 @@ public function build(): Configuration
144164
$this->composerJsonTemplateDir,
145165
$this->readmeMdTemplateDir,
146166
$this->httpMessage,
147-
$this->container
167+
$this->container,
168+
$this->includeTags,
169+
$this->excludeTags
148170
);
149171
}
150172
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file was generated by docler-labs/api-client-generator.
7+
*
8+
* Do not edit it manually.
9+
*/
10+
11+
namespace Test;
12+
13+
use Psr\Container\ContainerInterface;
14+
use Psr\Http\Client\ClientInterface;
15+
use Psr\Http\Message\ResponseInterface;
16+
use Test\Request\AddPetRequest;
17+
use Test\Request\CountPetsRequest;
18+
use Test\Request\FindPetsRequest;
19+
use Test\Request\Mapper\RequestMapperInterface;
20+
use Test\Request\RequestInterface;
21+
use Test\Response\ResponseHandler;
22+
use Test\Schema\Mapper\PetCollectionMapper;
23+
use Test\Schema\Mapper\PetMapper;
24+
use Test\Schema\Pet;
25+
use Test\Schema\PetCollection;
26+
27+
class SwaggerPetstoreClient
28+
{
29+
public function __construct(private readonly ClientInterface $client, private readonly ContainerInterface $container)
30+
{
31+
}
32+
33+
public function sendRequest(RequestInterface $request): ResponseInterface
34+
{
35+
return $this->client->sendRequest($this->container->get(RequestMapperInterface::class)->map($request));
36+
}
37+
38+
public function findPets(FindPetsRequest $request): PetCollection
39+
{
40+
$response = $this->handleResponse($this->sendRequest($request));
41+
42+
return $this->container->get(PetCollectionMapper::class)->toSchema($response);
43+
}
44+
45+
public function addPet(AddPetRequest $request): Pet
46+
{
47+
$response = $this->handleResponse($this->sendRequest($request));
48+
49+
return $this->container->get(PetMapper::class)->toSchema($response);
50+
}
51+
52+
public function countPets(CountPetsRequest $request): void
53+
{
54+
$this->handleResponse($this->sendRequest($request));
55+
}
56+
57+
protected function handleResponse(ResponseInterface $response)
58+
{
59+
return $this->container->get(ResponseHandler::class)->handle($response);
60+
}
61+
}

test/suite/functional/Generator/Client/petstore.yaml

+9-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ paths:
99
/pets:
1010
get:
1111
operationId: find-pets
12+
tags:
13+
- tag1
1214
parameters:
1315
- name: tags
1416
in: query
@@ -43,6 +45,8 @@ paths:
4345
$ref: '#/components/schemas/Error'
4446
post:
4547
operationId: addPet
48+
tags:
49+
- tag1
4650
requestBody:
4751
description: Pet to add to the store
4852
required: true
@@ -66,7 +70,7 @@ paths:
6670
head:
6771
operationId: countPets
6872
tags:
69-
- Pet
73+
- tag2
7074
responses:
7175
200:
7276
description: Count pets
@@ -78,6 +82,8 @@ paths:
7882
/pets/{id}:
7983
get:
8084
operationId: findPetById
85+
tags:
86+
- tag3
8187
parameters:
8288
- name: id
8389
in: path
@@ -102,6 +108,8 @@ paths:
102108
/pets/{id}/pet-food/{food_id}:
103109
delete:
104110
description: no operation id provided
111+
tags:
112+
- tag4
105113
parameters:
106114
- name: id
107115
in: path

test/suite/functional/Generator/ClientGeneratorTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ public function exampleProvider(): array
5252
self::BASE_NAMESPACE . '\\MultipleResponsesClient',
5353
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->build(),
5454
],
55+
'Basic schema with php 8.1 and include tags' => [
56+
'/Client/petstore.yaml',
57+
'/Client/SwaggerPetstoreClientWithTags.php',
58+
self::BASE_NAMESPACE . '\\SwaggerPetstoreClient',
59+
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->withIncludeTags(['tag1', 'tag2'])->build(),
60+
],
61+
'Basic schema with php 8.1 and exclude tags' => [
62+
'/Client/petstore.yaml',
63+
'/Client/SwaggerPetstoreClientWithTags.php',
64+
self::BASE_NAMESPACE . '\\SwaggerPetstoreClient',
65+
ConfigurationBuilder::fake()->withPhpVersion(PhpVersion::VERSION_PHP81)->withExcludeTags(['tag3', 'tag4'])->build(),
66+
],
5567
];
5668
}
5769

test/suite/unit/Input/ConfigurationTest.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ public function testValidConfiguration(
2727
string $composerJsonTemplateDir,
2828
string $readmeMdTemplateDir,
2929
string $httpMessage,
30-
string $container
30+
string $container,
31+
array $includeTags,
32+
array $excludeTags
3133
): void {
3234
$sut = new Configuration(
3335
$openapiFilePath,
@@ -41,7 +43,9 @@ public function testValidConfiguration(
4143
$composerJsonTemplateDir,
4244
$readmeMdTemplateDir,
4345
$httpMessage,
44-
$container
46+
$container,
47+
$includeTags,
48+
$excludeTags
4549
);
4650

4751
self::assertEquals($openapiFilePath, $sut->specificationFilePath);
@@ -56,6 +60,8 @@ public function testValidConfiguration(
5660
self::assertEquals($readmeMdTemplateDir, $sut->readmeMdTemplateDir);
5761
self::assertEquals($httpMessage, $sut->httpMessage);
5862
self::assertEquals($container, $sut->container);
63+
self::assertEquals($includeTags, $sut->includeTags);
64+
self::assertEquals($excludeTags, $sut->excludeTags);
5965
}
6066

6167
public function validConfigurationOptions(): array
@@ -74,6 +80,8 @@ public function validConfigurationOptions(): array
7480
__DIR__,
7581
Configuration::DEFAULT_HTTP_MESSAGE,
7682
Configuration::DEFAULT_CONTAINER,
83+
['tag1', 'tag2'],
84+
['tag3', 'tag4'],
7785
],
7886
];
7987
}

0 commit comments

Comments
 (0)