Skip to content

Commit cefb24c

Browse files
authored
Resolve: Generator crash when using reference inside an object cebe#163 (cebe#166)
Fix cebe#163 PR in fork - SOHELAHMED7#31
2 parents 06f5166 + c100572 commit cefb24c

File tree

13 files changed

+447
-9
lines changed

13 files changed

+447
-9
lines changed

src/lib/openapi/ResponseSchema.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ protected static function isObjectSchema($schema): bool
3535

3636
protected static function isArraySchemaWithRefItems($schema): bool
3737
{
38-
return $schema->type === 'array' && isset($schema->items) && $schema->items instanceof Reference;
38+
return isset($schema->items) && $schema->items instanceof Reference &&
39+
(isset($schema->type) && $schema->type === 'array');
3940
}
4041

4142
protected static function hasAttributesReference($schema):bool

tests/docker/Dockerfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ RUN echo "xdebug.idekey=PHP_STORM" >> $XDEBUGINI_PATH && \
7272
echo "xdebug.start_with_request=yes" >> $XDEBUGINI_PATH && \
7373
echo "xdebug.discover_client_host=1" >> $XDEBUGINI_PATH && \
7474
echo "xdebug.client_port=9003" >> $XDEBUGINI_PATH && \
75-
echo "xdebug.client_host=host.docker.internal" >> $XDEBUGINI_PATH
75+
echo "xdebug.client_host=host.docker.internal" >> $XDEBUGINI_PATH && \
76+
echo "xdebug.log_level=0" >> $XDEBUGINI_PATH
77+
# to get rid of lot of message `Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9003 (fallback through xdebug.client_host/xdebug.client_port).` in CLI during testing, above setting (log_level=0) is applied. Remove it when xdebug is used
78+
# reference: https://community.localwp.com/t/xdebug-step-debug-could-not-connect-to-debugging-client-flooding-logs/34550/4
79+
# scope to apply better solutions (https://stackoverflow.com/questions/64878376/xdebug-step-debug-could-not-connect-to-debugging-client)
80+
7681

7782
WORKDIR /app
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
return [
4+
'openApiPath' => '@specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/index.yaml',
5+
'generateUrls' => true,
6+
'generateModels' => true,
7+
'excludeModels' => [
8+
'Error',
9+
],
10+
'generateControllers' => true,
11+
'generateMigrations' => true,
12+
'generateModelFaker' => true, // `generateModels` must be `true` in orde to use `generateModelFaker` as `true`
13+
];
14+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
openapi: 3.0.3
2+
# Edit this schema and start your project
3+
# This is sample schema
4+
# To generate code which is based on this schema
5+
# run commands mentioned Development section in README.md file
6+
info:
7+
title: 'Proxy-Service'
8+
description: ""
9+
version: 1.0.0
10+
contact:
11+
name: '...'
12+
13+
servers:
14+
- url: 'http://localhost:9937'
15+
description: 'Local Dev API'
16+
17+
components:
18+
schemas:
19+
Contact:
20+
type: object
21+
required:
22+
- id
23+
properties:
24+
id:
25+
type: integer
26+
responses:
27+
Contact:
28+
description: 'Returns one contact by ID.'
29+
content:
30+
application/vnd.api+json:
31+
schema:
32+
type: object
33+
properties:
34+
data:
35+
$ref: '#/components/schemas/Contact'
36+
Contacts:
37+
description: 'Returns contacts.'
38+
content:
39+
application/vnd.api+json:
40+
schema:
41+
type: object
42+
properties:
43+
data:
44+
type: array
45+
items:
46+
$ref: '#/components/schemas/Contact'
47+
48+
49+
50+
paths:
51+
'/account/{accountId}/contacts':
52+
parameters:
53+
- name: accountId
54+
in: path
55+
description: ID of Account.
56+
required: true
57+
schema:
58+
type: integer
59+
60+
get:
61+
operationId: listAccountContacts
62+
summary: List all Account's contacts
63+
description: Returns all contacts for a account.
64+
responses:
65+
'200':
66+
$ref: '#/components/responses/Contacts'
67+
'403':
68+
description: Response if the currently authenticated user has no access to this Account.
69+
tags:
70+
- Contacts
71+
72+
'/account/{accountId}/contacts/{contactId}':
73+
parameters:
74+
- name: accountId
75+
in: path
76+
description: ID of Account.
77+
required: true
78+
schema:
79+
type: integer
80+
- name: contactId
81+
in: path
82+
description: ID of Contact.
83+
required: true
84+
schema:
85+
type: integer
86+
87+
get:
88+
operationId: getAccountContact
89+
summary: List a Account's contact
90+
description: Returns a contacts for a account specified by ID.
91+
responses:
92+
'200':
93+
$ref: '#/components/responses/Contact'
94+
'403':
95+
description: Response if the currently authenticated user has no access to this Account.
96+
tags:
97+
- Contacts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
/**
3+
* OpenAPI UrlRules
4+
*
5+
* This file is auto generated.
6+
*/
7+
return [
8+
'GET account/<accountId:\d+>/contacts' => 'contact/list-for-account',
9+
'GET account/<accountId:\d+>/contacts/<contactId:\d+>' => 'contact/view-for-account',
10+
'account/<accountId:\d+>/contacts' => 'contact/options',
11+
'account/<accountId:\d+>/contacts/<contactId:\d+>' => 'contact/options',
12+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace app\controllers;
4+
5+
class ContactController extends \app\controllers\base\ContactController
6+
{
7+
8+
public function checkAccess($action, $model = null, $params = [])
9+
{
10+
//TODO implement checkAccess
11+
}
12+
13+
public function actionListForAccount($accountId)
14+
{
15+
//TODO implement actionListForAccount
16+
}
17+
18+
public function actionViewForAccount($accountId, $contactId)
19+
{
20+
//TODO implement actionViewForAccount
21+
}
22+
23+
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace app\controllers\base;
4+
5+
abstract class ContactController extends \yii\rest\Controller
6+
{
7+
public function actions()
8+
{
9+
return [
10+
'options' => [
11+
'class' => \yii\rest\OptionsAction::class,
12+
],
13+
];
14+
}
15+
16+
/**
17+
* Checks the privilege of the current user.
18+
*
19+
* This method checks whether the current user has the privilege
20+
* to run the specified action against the specified data model.
21+
* If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
22+
*
23+
* @param string $action the ID of the action to be executed
24+
* @param object $model the model to be accessed. If null, it means no specific model is being accessed.
25+
* @param array $params additional parameters
26+
* @throws \yii\web\ForbiddenHttpException if the user does not have access
27+
*/
28+
abstract public function checkAccess($action, $model = null, $params = []);
29+
30+
abstract public function actionListForAccount($accountId);
31+
32+
abstract public function actionViewForAccount($accountId, $contactId);
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/**
4+
* Table for Contact
5+
*/
6+
class m200000_000000_create_table_contacts extends \yii\db\Migration
7+
{
8+
public function up()
9+
{
10+
$this->createTable('{{%contacts}}', [
11+
'id' => $this->primaryKey(),
12+
]);
13+
}
14+
15+
public function down()
16+
{
17+
$this->dropTable('{{%contacts}}');
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
namespace app\models;
4+
5+
use Faker\Factory as FakerFactory;
6+
use Faker\Generator;
7+
use Faker\UniqueGenerator;
8+
9+
/**
10+
* Base fake data generator
11+
*/
12+
abstract class BaseModelFaker
13+
{
14+
/**
15+
* @var Generator
16+
*/
17+
protected $faker;
18+
/**
19+
* @var UniqueGenerator
20+
*/
21+
protected $uniqueFaker;
22+
23+
public function __construct()
24+
{
25+
$this->faker = FakerFactory::create(str_replace('-', '_', \Yii::$app->language));
26+
$this->uniqueFaker = new UniqueGenerator($this->faker);
27+
}
28+
29+
abstract public function generateModel($attributes = []);
30+
31+
public function getFaker():Generator
32+
{
33+
return $this->faker;
34+
}
35+
36+
public function getUniqueFaker():UniqueGenerator
37+
{
38+
return $this->uniqueFaker;
39+
}
40+
41+
public function setFaker(Generator $faker):void
42+
{
43+
$this->faker = $faker;
44+
}
45+
46+
public function setUniqueFaker(UniqueGenerator $faker):void
47+
{
48+
$this->uniqueFaker = $faker;
49+
}
50+
51+
/**
52+
* Generate and return model
53+
* @param array|callable $attributes
54+
* @param UniqueGenerator|null $uniqueFaker
55+
* @return \yii\db\ActiveRecord
56+
* @example MyFaker::makeOne(['user_id' => 1, 'title' => 'foo']);
57+
* @example MyFaker::makeOne( function($model, $faker) {
58+
* $model->scenario = 'create';
59+
* $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]);
60+
* return $model;
61+
* });
62+
*/
63+
public static function makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null)
64+
{
65+
$fakeBuilder = new static();
66+
if ($uniqueFaker !== null) {
67+
$fakeBuilder->setUniqueFaker($uniqueFaker);
68+
}
69+
$model = $fakeBuilder->generateModel($attributes);
70+
return $model;
71+
}
72+
73+
/**
74+
* Generate, save and return model
75+
* @param array|callable $attributes
76+
* @param UniqueGenerator|null $uniqueFaker
77+
* @return \yii\db\ActiveRecord
78+
* @example MyFaker::saveOne(['user_id' => 1, 'title' => 'foo']);
79+
* @example MyFaker::saveOne( function($model, $faker) {
80+
* $model->scenario = 'create';
81+
* $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]);
82+
* return $model;
83+
* });
84+
*/
85+
public static function saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null)
86+
{
87+
$model = static::makeOne($attributes, $uniqueFaker);
88+
$model->save();
89+
return $model;
90+
}
91+
92+
/**
93+
* Generate and return multiple models
94+
* @param int $number
95+
* @param array|callable $commonAttributes
96+
* @return \yii\db\ActiveRecord[]|array
97+
* @example TaskFaker::make(5, ['project_id'=>1, 'user_id' => 2]);
98+
* @example TaskFaker::make(5, function($model, $faker, $uniqueFaker) {
99+
* $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]);
100+
* return $model;
101+
* });
102+
*/
103+
public static function make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array
104+
{
105+
if ($number < 1) {
106+
return [];
107+
}
108+
$fakeBuilder = new static();
109+
if ($uniqueFaker !== null) {
110+
$fakeBuilder->setUniqueFaker($uniqueFaker);
111+
}
112+
return array_map(function () use ($commonAttributes, $fakeBuilder) {
113+
$model = $fakeBuilder->generateModel($commonAttributes);
114+
return $model;
115+
}, range(0, $number -1));
116+
}
117+
118+
/**
119+
* Generate, save and return multiple models
120+
* @param int $number
121+
* @param array|callable $commonAttributes
122+
* @return \yii\db\ActiveRecord[]|array
123+
* @example TaskFaker::save(5, ['project_id'=>1, 'user_id' => 2]);
124+
* @example TaskFaker::save(5, function($model, $faker, $uniqueFaker) {
125+
* $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]);
126+
* return $model;
127+
* });
128+
*/
129+
public static function save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array
130+
{
131+
if ($number < 1) {
132+
return [];
133+
}
134+
$fakeBuilder = new static();
135+
if ($uniqueFaker !== null) {
136+
$fakeBuilder->setUniqueFaker($uniqueFaker);
137+
}
138+
return array_map(function () use ($commonAttributes, $fakeBuilder) {
139+
$model = $fakeBuilder->generateModel($commonAttributes);
140+
$model->save();
141+
return $model;
142+
}, range(0, $number -1));
143+
}
144+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace app\models;
4+
5+
class Contact extends \app\models\base\Contact
6+
{
7+
8+
9+
}
10+

0 commit comments

Comments
 (0)