Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve: Invalid schema reference error #75

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/lib/AttributeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,13 @@ protected function resolvePropertyRef(PropertySchema $property, Attribute $attri
$attribute->setPhpType($fkProperty->guessPhpType())
->setDbType($fkProperty->guessDbType(true))
->setSize($fkProperty->getMaxLength())
->setDescription($fkProperty->getAttr('description'))
->setDescription($fkProperty->getAttr('description', ''))
->setDefault($fkProperty->guessDefault())
->setLimits($min, $max, $fkProperty->getMinLength());

if ($fkProperty->hasEnum()) {
$attribute->setEnumValues($fkProperty->getAttr('enum'));
}
$this->attributes[$property->getName()] =
$attribute->setFakerStub($this->guessFakerStub($attribute, $fkProperty));
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/migrations/BaseMigrationBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ public function tmpSaveNewCol(string $tableAlias, \cebe\yii2openapi\db\ColumnSch

Yii::$app->db->createCommand()->createTable($tmpTableName, $column)->execute();
if (ApiGenerator::isPostgres() && $columnSchema->comment) {
Yii::$app->db->createCommand("COMMENT ON COLUMN $tmpTableName.$columnSchema->name IS {$this->db->quoteValue($columnSchema->comment)}")->execute();
Yii::$app->db->createCommand("COMMENT ON COLUMN $tmpTableName.\"$columnSchema->name\" IS {$this->db->quoteValue($columnSchema->comment)}")->execute();
}

$table = Yii::$app->db->getTableSchema($tmpTableName);
Expand Down
14 changes: 12 additions & 2 deletions src/lib/openapi/PropertySchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class PropertySchema

/** @var string $refPointer */
private $refPointer;
private $uri;

/** @var \cebe\yii2openapi\lib\openapi\ComponentSchema $refSchema */
private $refSchema;
Expand Down Expand Up @@ -170,6 +171,7 @@ private function initReference():void
{
$this->isReference = true;
$this->refPointer = $this->property->getJsonReference()->getJsonPointer()->getPointer();
$this->uri = $this->property->getJsonReference()->getDocumentUri();
$refSchemaName = $this->getRefSchemaName();
if ($this->isRefPointerToSelf()) {
$this->refSchema = $this->schema;
Expand All @@ -194,6 +196,7 @@ private function initItemsReference():void
return;
}
$this->refPointer = $items->getJsonReference()->getJsonPointer()->getPointer();
$this->uri = $items->getJsonReference()->getDocumentUri();
if ($this->isRefPointerToSelf()) {
$this->refSchema = $this->schema;
} elseif ($this->isRefPointerToSchema()) {
Expand Down Expand Up @@ -264,7 +267,9 @@ public function getSelfTargetProperty():?PropertySchema

public function isRefPointerToSchema():bool
{
return $this->refPointer && strpos($this->refPointer, self::REFERENCE_PATH) === 0;
return $this->refPointer &&
((strpos($this->refPointer, self::REFERENCE_PATH) === 0) ||
(str_ends_with($this->uri, '.yml')) || (str_ends_with($this->uri, '.yaml')));
}

public function isRefPointerToSelf():bool
Expand Down Expand Up @@ -300,8 +305,13 @@ public function getRefSchemaName():string
$pattern = strpos($this->refPointer, '/properties/') !== false ?
'~^'.self::REFERENCE_PATH.'(?<schemaName>.+)/properties/(?<propName>.+)$~'
: '~^'.self::REFERENCE_PATH.'(?<schemaName>.+)$~';
$separateFilePattern = '/((\.\/)*)(?<schemaName>.+)(\.)(yml|yaml)(.*)/'; # https://github.com/php-openapi/yii2-openapi/issues/74
if (!\preg_match($pattern, $this->refPointer, $matches)) {
throw new InvalidDefinitionException('Invalid schema reference');
if (!\preg_match($separateFilePattern, $this->uri, $separateFilePatternMatches)) {
throw new InvalidDefinitionException('Invalid schema reference');
} else {
return $separateFilePatternMatches['schemaName'];
}
}
return $matches['schemaName'];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Product:
title: Product
x-table: products
type: object

required:
- id
- vat_rate

properties:
id:
type: integer
vat_rate:
type: string
enum:
- standard
- none
default: standard
13 changes: 13 additions & 0 deletions tests/specs/issue_fix/74_invalid_schema_reference_error/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

return [
'openApiPath' => '@specs/issue_fix/74_invalid_schema_reference_error/index.yaml',
'generateUrls' => false,
'generateModels' => true,
'excludeModels' => [
'Error',
],
'generateControllers' => false,
'generateMigrations' => true,
'generateModelFaker' => true, // `generateModels` must be `true` in order to use `generateModelFaker` as `true`
];
25 changes: 25 additions & 0 deletions tests/specs/issue_fix/74_invalid_schema_reference_error/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Invalid schema reference error \#74
paths:
/:
get:
responses:
'200':
description: The information

components:
schemas:
Invoice:
type: object
required:
- vat_rate
properties:
id:
type: integer
vat_rate:
# $ref: '#/components/schemas/Product/properties/vat_rate' # issue is not observed
$ref: './Product.yaml#/properties/vat_rate' # issue is observed
Product:
$ref: ./Product.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

/**
* Table for Invoice
*/
class m200000_000000_create_table_invoices extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%invoices}}', [
'id' => $this->primaryKey(),
'vat_rate' => 'enum("standard", "none") NOT NULL DEFAULT \'standard\'',
]);
}

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

/**
* Table for Product
*/
class m200000_000001_create_table_products extends \yii\db\Migration
{
public function up()
{
$this->createTable('{{%products}}', [
'id' => $this->primaryKey(),
'vat_rate' => 'enum("standard", "none") NOT NULL DEFAULT \'standard\'',
]);
}

public function down()
{
$this->dropTable('{{%products}}');
}
}
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 Invoice extends \app\models\base\Invoice
{


}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
namespace app\models;

use Faker\UniqueGenerator;

/**
* Fake data generator for Invoice
* @method static Invoice makeOne($attributes = [], ?UniqueGenerator $uniqueFaker = null);
* @method static Invoice saveOne($attributes = [], ?UniqueGenerator $uniqueFaker = null);
* @method static Invoice[] make(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null)
* @method static Invoice[] save(int $number, $commonAttributes = [], ?UniqueGenerator $uniqueFaker = null)
*/
class InvoiceFaker extends BaseModelFaker
{

/**
* @param array|callable $attributes
* @return Invoice|\yii\db\ActiveRecord
* @example
* $model = (new PostFaker())->generateModels(['author_id' => 1]);
* $model = (new PostFaker())->generateModels(function($model, $faker, $uniqueFaker) {
* $model->scenario = 'create';
* $model->author_id = 1;
* return $model;
* });
**/
public function generateModel($attributes = [])
{
$faker = $this->faker;
$uniqueFaker = $this->uniqueFaker;
$model = new Invoice();
//$model->id = $uniqueFaker->numberBetween(0, 1000000);
$model->vat_rate = $faker->randomElement(['standard','none']);
if (!is_callable($attributes)) {
$model->setAttributes($attributes, false);
} else {
$model = $attributes($model, $faker, $uniqueFaker);
}
return $model;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace app\models;

class Product extends \app\models\base\Product
{


}

Loading
Loading