Skip to content

Commit

Permalink
Resolve: Generator crash when using reference inside an object cebe#163
Browse files Browse the repository at this point in the history
… (cebe#166)

Fix cebe#163

PR in fork - #31
  • Loading branch information
cebe authored Apr 29, 2024
2 parents 06f5166 + c100572 commit cefb24c
Show file tree
Hide file tree
Showing 13 changed files with 447 additions and 9 deletions.
3 changes: 2 additions & 1 deletion src/lib/openapi/ResponseSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ protected static function isObjectSchema($schema): bool

protected static function isArraySchemaWithRefItems($schema): bool
{
return $schema->type === 'array' && isset($schema->items) && $schema->items instanceof Reference;
return isset($schema->items) && $schema->items instanceof Reference &&
(isset($schema->type) && $schema->type === 'array');
}

protected static function hasAttributesReference($schema):bool
Expand Down
7 changes: 6 additions & 1 deletion tests/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ RUN echo "xdebug.idekey=PHP_STORM" >> $XDEBUGINI_PATH && \
echo "xdebug.start_with_request=yes" >> $XDEBUGINI_PATH && \
echo "xdebug.discover_client_host=1" >> $XDEBUGINI_PATH && \
echo "xdebug.client_port=9003" >> $XDEBUGINI_PATH && \
echo "xdebug.client_host=host.docker.internal" >> $XDEBUGINI_PATH
echo "xdebug.client_host=host.docker.internal" >> $XDEBUGINI_PATH && \
echo "xdebug.log_level=0" >> $XDEBUGINI_PATH
# 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
# reference: https://community.localwp.com/t/xdebug-step-debug-could-not-connect-to-debugging-client-flooding-logs/34550/4
# scope to apply better solutions (https://stackoverflow.com/questions/64878376/xdebug-step-debug-could-not-connect-to-debugging-client)


WORKDIR /app
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

return [
'openApiPath' => '@specs/issue_fix/163_generator_crash_when_using_reference_inside_an_object/index.yaml',
'generateUrls' => true,
'generateModels' => true,
'excludeModels' => [
'Error',
],
'generateControllers' => true,
'generateMigrations' => true,
'generateModelFaker' => true, // `generateModels` must be `true` in orde to use `generateModelFaker` as `true`
];

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
openapi: 3.0.3
# Edit this schema and start your project
# This is sample schema
# To generate code which is based on this schema
# run commands mentioned Development section in README.md file
info:
title: 'Proxy-Service'
description: ""
version: 1.0.0
contact:
name: '...'
email: [email protected]
servers:
- url: 'http://localhost:9937'
description: 'Local Dev API'

components:
schemas:
Contact:
type: object
required:
- id
properties:
id:
type: integer
responses:
Contact:
description: 'Returns one contact by ID.'
content:
application/vnd.api+json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/Contact'
Contacts:
description: 'Returns contacts.'
content:
application/vnd.api+json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'



paths:
'/account/{accountId}/contacts':
parameters:
- name: accountId
in: path
description: ID of Account.
required: true
schema:
type: integer

get:
operationId: listAccountContacts
summary: List all Account's contacts
description: Returns all contacts for a account.
responses:
'200':
$ref: '#/components/responses/Contacts'
'403':
description: Response if the currently authenticated user has no access to this Account.
tags:
- Contacts

'/account/{accountId}/contacts/{contactId}':
parameters:
- name: accountId
in: path
description: ID of Account.
required: true
schema:
type: integer
- name: contactId
in: path
description: ID of Contact.
required: true
schema:
type: integer

get:
operationId: getAccountContact
summary: List a Account's contact
description: Returns a contacts for a account specified by ID.
responses:
'200':
$ref: '#/components/responses/Contact'
'403':
description: Response if the currently authenticated user has no access to this Account.
tags:
- Contacts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
/**
* OpenAPI UrlRules
*
* This file is auto generated.
*/
return [
'GET account/<accountId:\d+>/contacts' => 'contact/list-for-account',
'GET account/<accountId:\d+>/contacts/<contactId:\d+>' => 'contact/view-for-account',
'account/<accountId:\d+>/contacts' => 'contact/options',
'account/<accountId:\d+>/contacts/<contactId:\d+>' => 'contact/options',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace app\controllers;

class ContactController extends \app\controllers\base\ContactController
{

public function checkAccess($action, $model = null, $params = [])
{
//TODO implement checkAccess
}

public function actionListForAccount($accountId)
{
//TODO implement actionListForAccount
}

public function actionViewForAccount($accountId, $contactId)
{
//TODO implement actionViewForAccount
}


}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace app\controllers\base;

abstract class ContactController extends \yii\rest\Controller
{
public function actions()
{
return [
'options' => [
'class' => \yii\rest\OptionsAction::class,
],
];
}

/**
* Checks the privilege of the current user.
*
* This method checks whether the current user has the privilege
* to run the specified action against the specified data model.
* If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
*
* @param string $action the ID of the action to be executed
* @param object $model the model to be accessed. If null, it means no specific model is being accessed.
* @param array $params additional parameters
* @throws \yii\web\ForbiddenHttpException if the user does not have access
*/
abstract public function checkAccess($action, $model = null, $params = []);

abstract public function actionListForAccount($accountId);

abstract public function actionViewForAccount($accountId, $contactId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/**
* Table for Contact
*/
class m200000_000000_create_table_contacts extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%contacts}}', [
'id' => $this->primaryKey(),
]);
}

public function down()
{
$this->dropTable('{{%contacts}}');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

namespace app\models;

use Faker\Factory as FakerFactory;
use Faker\Generator;
use Faker\UniqueGenerator;

/**
* Base fake data generator
*/
abstract class BaseModelFaker
{
/**
* @var Generator
*/
protected $faker;
/**
* @var UniqueGenerator
*/
protected $uniqueFaker;

public function __construct()
{
$this->faker = FakerFactory::create(str_replace('-', '_', \Yii::$app->language));
$this->uniqueFaker = new UniqueGenerator($this->faker);
}

abstract public function generateModel($attributes = []);

public function getFaker():Generator
{
return $this->faker;
}

public function getUniqueFaker():UniqueGenerator
{
return $this->uniqueFaker;
}

public function setFaker(Generator $faker):void
{
$this->faker = $faker;
}

public function setUniqueFaker(UniqueGenerator $faker):void
{
$this->uniqueFaker = $faker;
}

/**
* Generate and return model
* @param array|callable $attributes
* @param UniqueGenerator|null $uniqueFaker
* @return \yii\db\ActiveRecord
* @example MyFaker::makeOne(['user_id' => 1, 'title' => 'foo']);
* @example MyFaker::makeOne( function($model, $faker) {
* $model->scenario = 'create';
* $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]);
* return $model;
* });
*/
public static function makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null)
{
$fakeBuilder = new static();
if ($uniqueFaker !== null) {
$fakeBuilder->setUniqueFaker($uniqueFaker);
}
$model = $fakeBuilder->generateModel($attributes);
return $model;
}

/**
* Generate, save and return model
* @param array|callable $attributes
* @param UniqueGenerator|null $uniqueFaker
* @return \yii\db\ActiveRecord
* @example MyFaker::saveOne(['user_id' => 1, 'title' => 'foo']);
* @example MyFaker::saveOne( function($model, $faker) {
* $model->scenario = 'create';
* $model->setAttributes(['user_id' => 1, 'title' => $faker->sentence]);
* return $model;
* });
*/
public static function saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null)
{
$model = static::makeOne($attributes, $uniqueFaker);
$model->save();
return $model;
}

/**
* Generate and return multiple models
* @param int $number
* @param array|callable $commonAttributes
* @return \yii\db\ActiveRecord[]|array
* @example TaskFaker::make(5, ['project_id'=>1, 'user_id' => 2]);
* @example TaskFaker::make(5, function($model, $faker, $uniqueFaker) {
* $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]);
* return $model;
* });
*/
public static function make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array
{
if ($number < 1) {
return [];
}
$fakeBuilder = new static();
if ($uniqueFaker !== null) {
$fakeBuilder->setUniqueFaker($uniqueFaker);
}
return array_map(function () use ($commonAttributes, $fakeBuilder) {
$model = $fakeBuilder->generateModel($commonAttributes);
return $model;
}, range(0, $number -1));
}

/**
* Generate, save and return multiple models
* @param int $number
* @param array|callable $commonAttributes
* @return \yii\db\ActiveRecord[]|array
* @example TaskFaker::save(5, ['project_id'=>1, 'user_id' => 2]);
* @example TaskFaker::save(5, function($model, $faker, $uniqueFaker) {
* $model->setAttributes(['name' => $uniqueFaker->username, 'state'=>$faker->boolean(20)]);
* return $model;
* });
*/
public static function save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null):array
{
if ($number < 1) {
return [];
}
$fakeBuilder = new static();
if ($uniqueFaker !== null) {
$fakeBuilder->setUniqueFaker($uniqueFaker);
}
return array_map(function () use ($commonAttributes, $fakeBuilder) {
$model = $fakeBuilder->generateModel($commonAttributes);
$model->save();
return $model;
}, range(0, $number -1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace app\models;

class Contact extends \app\models\base\Contact
{


}

Loading

0 comments on commit cefb24c

Please sign in to comment.