Skip to content

Commit 35af3a7

Browse files
authored
Merge pull request #25 from DoclerLabs/fix-nullable
Fix nullable
2 parents 1a12200 + c55206c commit 35af3a7

34 files changed

+191
-675
lines changed

.php_cs.php

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565
'binary_operator_spaces' => [
6666
'default' => 'align_single_space_minimal',
6767
],
68+
'no_superfluous_phpdoc_tags' => true,
69+
'no_empty_phpdoc' => true,
70+
'no_extra_blank_lines' => true,
6871
];
6972

7073
return Config::create()

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ 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+
## [4.1.0] - 2020-12-28
8+
### Fixed
9+
- Nullable mandatory DateTime properties in schema now work correctly without doing `new DateTimeImmutable(null)`
10+
- Proper nullable typehint and default value in properties for 7.4 schemas to avoid non initialized errors
11+
12+
### Changed
13+
- Changed default php-cs-fixer configuration to remove superfluous docblocks, to improve generated code readability
14+
15+
### Added
16+
- Makefile with standard routines to easy development / contribution (run tests, analyse code, fix code style)
17+
718
## [4.0.0] - 2020-11-26
819
### Changed
920
- Guzzle 6/7 dependency removed altogether in favor of PSR-18 interfaces for Http Client

CONTRIBUTING.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ If you're opening a PR, keep in mind Github will validate code style (PSR1/PSR2)
2828

2929
This generator works with PHP 7.4 so either you need to have it locally installed or you can use any php 7.4 docker image to run phpunit, phpstan, php-cs-fixer etc.
3030

31-
To make sure your code is following the correct style, run:
31+
To make sure your code is following the correct style, run (or check `make` to run these tasks through php 7.4 docker image):
3232

3333
```
34-
$ ./vendor/bin/php-cs-fixer fix src
34+
$ ./vendor/bin/php-cs-fixer fix .
3535
```
3636

3737
To statically analyse your code, run:
@@ -46,6 +46,24 @@ To run tests:
4646
$ ./vendor/bin/phpunit
4747
```
4848

49+
After doing your changes, you can validate it by using your modified generator with an actual Open API specification file. To do that, you must generate your own local Docker image of this generator and use it with any spec you may have:
50+
51+
```
52+
$ docker build -t my-api-client-generator .
53+
```
54+
55+
Then run the same command to build your API client but replacing `dhlabs/api-client-generator` with `my-api-client-generator`. For example:
56+
```
57+
docker run -it \
58+
-v {path-to-specification}/openapi.yaml:/openapi.yaml:ro \
59+
-v {path-to-client}/some-api-client:/client \
60+
-e NAMESPACE=Group\\SomeApiClient \
61+
-e OPENAPI=/openapi.yaml \
62+
-e OUTPUT_DIR=/client \
63+
-e PACKAGE=group/some-api-client \
64+
my-api-client-generator
65+
```
66+
4967
## Community
5068

5169
Discussions must take place on this repository's [Issues](https://github.com/DoclerLabs/api-client-generator/issues) and [Pull Requests](https://github.com/DoclerLabs/api-client-generator/pulls) sections. Anybody is welcome to join these conversations.

Makefile

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.PHONY: $(filter-out help, $(MAKECMDGOALS))
2+
.DEFAULT_GOAL := help
3+
4+
DOCKER_RUN=docker run -w /app -v $(shell pwd):/app php:7.4-cli-alpine php
5+
6+
help:
7+
@echo "\033[33mUsage:\033[0m\n make [target] [arg=\"val\"...]\n\n\033[33mTargets:\033[0m"
8+
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[32m%-10s\033[0m %s\n", $$1, $$2}'
9+
10+
test: ## run test suites
11+
$(DOCKER_RUN) vendor/bin/phpunit -c phpunit.xml.dist --colors=always
12+
13+
cs: ## fix code style
14+
$(DOCKER_RUN) vendor/bin/php-cs-fixer fix .
15+
16+
stan: ## statically analyse code
17+
$(DOCKER_RUN) vendor/bin/phpstan analyse src

README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# OpenAPI SDK generator - API client generator
22

3-
API client generator is a console application capable of auto-generating an API client based on OpenAPI specification according to PHP best practices, and your code style standards.
3+
API client generator is a console application capable of auto-generating a [PSR18](https://www.php-fig.org/psr/psr-18/)/[PSR7](https://www.php-fig.org/psr/psr-7/) compliant API client based on [OpenAPI v3](https://swagger.io/specification/) specification according to PHP best practices and your code style standards.
44

55
[![Build Status](https://travis-ci.org/DoclerLabs/api-client-generator.svg?branch=master)](https://travis-ci.org/DoclerLabs/api-client-generator)
66
[![Coverage Status](https://coveralls.io/repos/github/DoclerLabs/api-client-generator/badge.svg?branch=master)](https://coveralls.io/github/DoclerLabs/api-client-generator?branch=master)
@@ -9,7 +9,7 @@ API client generator is a console application capable of auto-generating an API
99
## Usage
1010
### With Docker
1111
```
12-
docker run -it \
12+
$ docker run -it \
1313
-v {path-to-specification}/openapi.yaml:/openapi.yaml:ro \
1414
-v {path-to-client}/some-api-client:/client \
1515
-e NAMESPACE=Group\\SomeApiClient \
@@ -45,5 +45,8 @@ The following environment variables are available:
4545
## Running tests
4646

4747
```bash
48-
docker run -w /app -v $(pwd):/app php:7.4-cli-alpine php /app/vendor/bin/phpunit -c /app/phpunit.xml.dist --colors=always
48+
$ composer install
49+
$ make test
4950
```
51+
52+
(check `make` for all available routines).

src/Ast/Builder/CodeBuilder.php

+12-2
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,23 @@ public function appendToAssociativeArray(Variable $arrayVar, Expr $key, Expr $va
129129
return new Expression($this->assign($this->getArrayItem($arrayVar, $key), $value));
130130
}
131131

132-
public function localProperty(string $name, string $type, string $docType, Expr $default = null): Property
133-
{
132+
public function localProperty(
133+
string $name,
134+
string $type,
135+
string $docType,
136+
bool $nullable = false,
137+
Expr $default = null
138+
): Property {
134139
$property = $this
135140
->property($name)
136141
->makePrivate();
137142

138143
if ($this->phpVersion->isPropertyTypeHintSupported()) {
144+
if ($nullable) {
145+
$property->setDefault(null);
146+
$type = '?' . $type;
147+
}
148+
139149
$property->setType($type);
140150
} else {
141151
$docComment = sprintf('/** @var %s */', $docType);

src/Generator/MutatorAccessorClassGeneratorAbstract.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ protected function generateProperty(Field $field): Property
8181
return $this->builder->localProperty(
8282
$field->getPhpVariableName(),
8383
$field->getPhpTypeHint(),
84-
$field->getPhpDocType()
84+
$field->getPhpDocType(),
85+
$field->isOptional() || $field->isNullable()
8586
);
8687
}
8788

src/Generator/SchemaGenerator.php

+1
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ protected function generateConstructor(Field $root): ?ClassMethod
9494
$paramsDoc[] = $this->builder
9595
->param($propertyField->getPhpVariableName())
9696
->setType($propertyField->getPhpTypeHint(), $propertyField->isNullable())
97+
->setDocBlockType($propertyField->getPhpDocType($propertyField->isNullable()))
9798
->getNode();
9899
}
99100
}

src/Generator/SchemaMapperGenerator.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,15 @@ protected function generateMapStatementsForObject(Field $root, Variable $payload
275275
);
276276
} elseif ($field->isDate()) {
277277
$this->addImport(DateTimeImmutable::class);
278-
$requiredVars[] = $this->builder->new('DateTimeImmutable', [$requiredResponseItems[$i]]);
278+
if ($field->isNullable()) {
279+
$requiredVars[] = $this->builder->ternary(
280+
$this->builder->notEquals($requiredResponseItems[$i], $this->builder->val(null)),
281+
$this->builder->new('DateTimeImmutable', [$requiredResponseItems[$i]]),
282+
$this->builder->val(null)
283+
);
284+
} else {
285+
$requiredVars[] = $this->builder->new('DateTimeImmutable', [$requiredResponseItems[$i]]);
286+
}
279287
} else {
280288
$requiredVars[] = $requiredResponseItems[$i];
281289
}

src/Output/Copy/Response/ResponseHandler.php

-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ public function __construct(BodySerializer $bodySerializer, ResponseExceptionFac
2020
$this->responseExceptionFactory = $exceptionFactory;
2121
}
2222

23-
/**
24-
* @param ResponseInterface $response
25-
*
26-
* @return array
27-
*/
2823
public function handle(ResponseInterface $response): array
2924
{
3025
$statusCode = $response->getStatusCode();

src/Output/Copy/Schema/Mapper/SchemaMapperInterface.php

-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
interface SchemaMapperInterface
88
{
99
/**
10-
* @param array $payload
11-
*
1210
* @return SerializableInterface
1311
*/
1412
public function toSchema(array $payload);

src/Output/Copy/Serializer/ContentType/Json/Json.php

-4
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ class Json
1111
* @param mixed $data Any data to encode.
1212
* @param int $options Optional settings to pass to json_encode (@see json_encode).
1313
*
14-
* @return string
15-
*
1614
* @throws JsonException
1715
*/
1816
public static function encode($data, int $options = 0): string
@@ -56,8 +54,6 @@ public static function getLastErrorMessage(): string
5654

5755
/**
5856
* @param int $errorCode Error code to check for (@uses JSON_ERROR_*).
59-
*
60-
* @return string
6157
*/
6258
public static function getErrorMessage(int $errorCode): string
6359
{

test/suite/functional/Generator/Client/SwaggerPetstoreClient.php

-33
Original file line numberDiff line numberDiff line change
@@ -32,75 +32,42 @@ class SwaggerPetstoreClient
3232
/** @var ContainerInterface */
3333
private $container;
3434

35-
/**
36-
* @param ClientInterface $client
37-
* @param ContainerInterface $container
38-
*/
3935
public function __construct(ClientInterface $client, ContainerInterface $container)
4036
{
4137
$this->client = $client;
4238
$this->container = $container;
4339
}
4440

45-
/**
46-
* @param RequestInterface $request
47-
*
48-
* @return ResponseInterface
49-
*/
5041
public function sendRequest(RequestInterface $request): ResponseInterface
5142
{
5243
return $this->client->sendRequest($this->container->get(RequestMapperInterface::class)->map($request));
5344
}
5445

55-
/**
56-
* @param FindPetsRequest $request
57-
*
58-
* @return PetCollection
59-
*/
6046
public function findPets(FindPetsRequest $request): PetCollection
6147
{
6248
return $this->container->get(PetCollectionMapper::class)->toSchema($this->handleResponse($this->sendRequest($request)));
6349
}
6450

65-
/**
66-
* @param AddPetRequest $request
67-
*
68-
* @return Pet
69-
*/
7051
public function addPet(AddPetRequest $request): Pet
7152
{
7253
return $this->container->get(PetMapper::class)->toSchema($this->handleResponse($this->sendRequest($request)));
7354
}
7455

75-
/**
76-
* @param CountPetsRequest $request
77-
*/
7856
public function countPets(CountPetsRequest $request): void
7957
{
8058
$this->handleResponse($this->sendRequest($request));
8159
}
8260

83-
/**
84-
* @param FindPetByIdRequest $request
85-
*
86-
* @return Pet
87-
*/
8861
public function findPetById(FindPetByIdRequest $request): Pet
8962
{
9063
return $this->container->get(PetMapper::class)->toSchema($this->handleResponse($this->sendRequest($request)));
9164
}
9265

93-
/**
94-
* @param DeletePetsIdPetFoodFoodIdRequest $request
95-
*/
9666
public function deletePetsIdPetFoodFoodId(DeletePetsIdPetFoodFoodIdRequest $request): void
9767
{
9868
$this->handleResponse($this->sendRequest($request));
9969
}
10070

101-
/**
102-
* @param ResponseInterface $response
103-
*/
10471
protected function handleResponse(ResponseInterface $response)
10572
{
10673
return $this->container->get(ResponseHandler::class)->handle($response);

test/suite/functional/Generator/ClientFactory/ClientFactoryDefault.php

-5
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515

1616
class SwaggerPetstoreClientFactory
1717
{
18-
/**
19-
* @param ClientInterface $client
20-
*
21-
* @return SwaggerPetstoreClient
22-
*/
2318
public function create(ClientInterface $client): SwaggerPetstoreClient
2419
{
2520
return new SwaggerPetstoreClient($client, $this->initContainer());

test/suite/functional/Generator/Request/GetResourcesRequest.php

-33
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,13 @@ class GetResourcesRequest implements RequestInterface
2121
/** @var int[]|null */
2222
private $filterByIds;
2323

24-
/**
25-
* @param int $filterById
26-
*
27-
* @return self
28-
*/
2924
public function setFilterById(int $filterById): self
3025
{
3126
$this->filterById = $filterById;
3227

3328
return $this;
3429
}
3530

36-
/**
37-
* @param string $filterByName
38-
*
39-
* @return self
40-
*/
4131
public function setFilterByName(string $filterByName): self
4232
{
4333
$this->filterByName = $filterByName;
@@ -47,8 +37,6 @@ public function setFilterByName(string $filterByName): self
4737

4838
/**
4939
* @param int[] $filterByIds
50-
*
51-
* @return self
5240
*/
5341
public function setFilterByIds(array $filterByIds): self
5442
{
@@ -57,33 +45,21 @@ public function setFilterByIds(array $filterByIds): self
5745
return $this;
5846
}
5947

60-
/**
61-
* @return string
62-
*/
6348
public function getContentType(): string
6449
{
6550
return '';
6651
}
6752

68-
/**
69-
* @return string
70-
*/
7153
public function getMethod(): string
7254
{
7355
return 'GET';
7456
}
7557

76-
/**
77-
* @return string
78-
*/
7958
public function getRoute(): string
8059
{
8160
return 'v1/resources';
8261
}
8362

84-
/**
85-
* @return array
86-
*/
8763
public function getQueryParameters(): array
8864
{
8965
return \array_map(static function ($value) {
@@ -93,25 +69,16 @@ public function getQueryParameters(): array
9369
}));
9470
}
9571

96-
/**
97-
* @return array
98-
*/
9972
public function getRawQueryParameters(): array
10073
{
10174
return ['filterById' => $this->filterById, 'filterByName' => $this->filterByName, 'filterByIds' => $this->filterByIds];
10275
}
10376

104-
/**
105-
* @return array
106-
*/
10777
public function getCookies(): array
10878
{
10979
return [];
11080
}
11181

112-
/**
113-
* @return array
114-
*/
11582
public function getHeaders(): array
11683
{
11784
return [];

0 commit comments

Comments
 (0)