Skip to content

Commit

Permalink
Merge branch 'main' into 226-docs-generation-fails-for-requests-that-…
Browse files Browse the repository at this point in the history
…utilise-custom-values
  • Loading branch information
romalytvynenko committed May 20, 2024
2 parents ba2e1d1 + 0d107f8 commit f69af67
Show file tree
Hide file tree
Showing 45 changed files with 1,123 additions and 240 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ testbench.yaml
vendor
node_modules
*-test.json
.phpunit.cache/
49 changes: 20 additions & 29 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
executionOrder="random"
failOnWarning="true"
failOnRisky="true"
failOnEmptyTestSuite="true"
>
<testsuites>
<testsuite name="Dedoc Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
</report>
</coverage>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" backupGlobals="false" bootstrap="vendor/autoload.php" colors="true" executionOrder="random" failOnWarning="true" failOnRisky="true" failOnEmptyTestSuite="true" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
<testsuites>
<testsuite name="Dedoc Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<report>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
</report>
</coverage>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
20 changes: 12 additions & 8 deletions src/GeneratorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ public function __construct(
private ?Closure $routeResolver = null,
private ?Closure $afterOpenApiGenerated = null,
) {
$this->routeResolver = $this->routeResolver ?? function (Route $route) {
$expectedDomain = $this->get('api_domain');

return Str::startsWith($route->uri, $this->get('api_path', 'api'))
&& (! $expectedDomain || $route->getDomain() === $expectedDomain);
};
}

public function config(array $config)
Expand All @@ -32,14 +26,24 @@ public function config(array $config)
public function routes(?Closure $routeResolver = null)
{
if (count(func_get_args()) === 0) {
return $this->routeResolver;
return $this->routeResolver ?: $this->defaultRoutesFilter(...);
}

$this->routeResolver = $routeResolver;
if ($routeResolver) {
$this->routeResolver = $routeResolver;
}

return $this;
}

private function defaultRoutesFilter(Route $route)
{
$expectedDomain = $this->get('api_domain');

return Str::startsWith($route->uri, $this->get('api_path', 'api'))
&& (! $expectedDomain || $route->getDomain() === $expectedDomain);
}

public function afterOpenApiGenerated(?Closure $afterOpenApiGenerated = null)
{
if (count(func_get_args()) === 0) {
Expand Down
7 changes: 5 additions & 2 deletions src/Infer/Analyzer/MethodAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Dedoc\Scramble\Infer\Context;
use Dedoc\Scramble\Infer\Definition\ClassDefinition;
use Dedoc\Scramble\Infer\Definition\FunctionLikeDefinition;
use Dedoc\Scramble\Infer\Handler\IndexBuildingHandler;
use Dedoc\Scramble\Infer\Reflector\ClassReflector;
use Dedoc\Scramble\Infer\Scope\Index;
use Dedoc\Scramble\Infer\Scope\NodeTypesResolver;
Expand All @@ -25,11 +26,12 @@ public function __construct(
) {
}

public function analyze(FunctionLikeDefinition $methodDefinition)
public function analyze(FunctionLikeDefinition $methodDefinition, array $indexBuilders = [])
{
$scope = $this->traverseClassMethod(
[$this->getClassReflector()->getMethod($methodDefinition->type->name)->getAstNode()],
$methodDefinition,
$indexBuilders,
);

$methodDefinition = $this->index
Expand All @@ -49,7 +51,7 @@ private function getClassReflector(): ClassReflector
return ClassReflector::make($this->classDefinition->name);
}

private function traverseClassMethod(array $nodes, FunctionLikeDefinition $methodDefinition)
private function traverseClassMethod(array $nodes, FunctionLikeDefinition $methodDefinition, array $indexBuilders = [])
{
$traverser = new NodeTraverser;

Expand All @@ -60,6 +62,7 @@ private function traverseClassMethod(array $nodes, FunctionLikeDefinition $metho
$nameResolver,
$scope = new Scope($this->index, new NodeTypesResolver(), new ScopeContext($this->classDefinition), $nameResolver),
Context::getInstance()->extensionsBroker->extensions,
[new IndexBuildingHandler($indexBuilders)],
));

$node = (new NodeFinder())
Expand Down
9 changes: 7 additions & 2 deletions src/Infer/Definition/ClassDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ public function isChildOf(string $className)
return $this->isInstanceOf($className) && $this->name !== $className;
}

public function getMethodDefinition(string $name, Scope $scope = new GlobalScope)
public function hasMethodDefinition(string $name): bool
{
return array_key_exists($name, $this->methods);
}

public function getMethodDefinition(string $name, Scope $scope = new GlobalScope, array $indexBuilders = [])
{
if (! array_key_exists($name, $this->methods)) {
return null;
Expand All @@ -57,7 +62,7 @@ public function getMethodDefinition(string $name, Scope $scope = new GlobalScope
$result = (new MethodAnalyzer(
$scope->index,
$this
))->analyze($methodDefinition);
))->analyze($methodDefinition, $indexBuilders);

$this->methodsScopes[$name] = $result->scope;
$this->methods[$name] = $result->definition;
Expand Down
26 changes: 26 additions & 0 deletions src/Infer/Handler/IndexBuildingHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Dedoc\Scramble\Infer\Handler;

use Dedoc\Scramble\Infer\Scope\Scope;
use PhpParser\Node;

class IndexBuildingHandler
{
public function __construct(
private array $indexBuilders,
) {
}

public function shouldHandle($node)
{
return true;
}

public function leave(Node $node, Scope $scope)
{
foreach ($this->indexBuilders as $indexBuilder) {
$indexBuilder->afterAnalyzedNode($scope, $node);
}
}
}
3 changes: 3 additions & 0 deletions src/Infer/Reflector/ClassReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public function getNameContext(): NameContext

$code = Str::before($content, $firstMatchedClassLikeString);

// Removes all comments.
$code = preg_replace('/\/\*(?:[^*]|\*+[^*\/])*\*+\/|(?<![:\'"])\/\/.*|(?<![:\'"])#.*/', '', $code);

$re = '/(namespace|use) ([.\s\S]*?);/m';
preg_match_all($re, $code, $matches);

Expand Down
7 changes: 5 additions & 2 deletions src/Infer/Reflector/MethodReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ public function getReflection(): ReflectionMethod
return new ReflectionMethod($this->className, $this->name);
}

public function getAstNode(): ClassMethod
/**
* @todo: Think if this method can actually return `null` or it should fail.
*/
public function getAstNode(): ?ClassMethod
{
if (! $this->methodNode) {
$className = class_basename($this->className);
Expand Down Expand Up @@ -92,6 +95,6 @@ public function beforeTraverse(array $nodes): ?array

public function getClassReflector(): ClassReflector
{
return ClassReflector::make($this->className);
return ClassReflector::make($this->getReflection()->class);
}
}
2 changes: 1 addition & 1 deletion src/Infer/Scope/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function getType(Node $node): Type

if ($node instanceof Node\Expr\Ternary) {
return Union::wrap([
$this->getType($node->if),
$this->getType($node->if ?? $node->cond),
$this->getType($node->else),
]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Infer/Services/FileParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static function getInstance(): static

public function parseContent(string $content): FileParserResult
{
return $this->cache[md5($content).'23'] ??= new FileParserResult(
return $this->cache[md5($content)] ??= new FileParserResult(
$statements = Arr::wrap($this->parser->parse($content)),
new FileNameResolver(new NameContext(new Throwing()))
);
Expand Down
10 changes: 5 additions & 5 deletions src/Infer/Services/ReferenceTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,17 @@ private function resolveNewCallReferenceType(Scope $scope, NewCallReferenceType

private function resolvePropertyFetchReferenceType(Scope $scope, PropertyFetchReferenceType $type)
{
$objectType = $this->resolve($scope, $type->object);

if (
($type->object instanceof ObjectType)
&& ! array_key_exists($type->object->name, $this->index->classesDefinitions)
&& ! $this->resolveUnknownClassResolver($type->object->name)
($objectType instanceof ObjectType)
&& ! array_key_exists($objectType->name, $this->index->classesDefinitions)
&& ! $this->resolveUnknownClassResolver($objectType->name)
) {
// Class is not indexed, and we simply cannot get an info from it.
return $type;
}

$objectType = $this->resolve($scope, $type->object);

if (
$objectType instanceof AbstractReferenceType
|| $objectType instanceof TemplateType
Expand Down
5 changes: 5 additions & 0 deletions src/Infer/SimpleTypeGetters/ScalarTypeGetter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Dedoc\Scramble\Infer\SimpleTypeGetters;

use Dedoc\Scramble\Support\Type\Literal\LiteralFloatType;
use Dedoc\Scramble\Support\Type\Literal\LiteralIntegerType;
use Dedoc\Scramble\Support\Type\Literal\LiteralStringType;
use Dedoc\Scramble\Support\Type\Type;
Expand All @@ -20,6 +21,10 @@ public function __invoke(Node\Scalar $node): Type
return new LiteralIntegerType($node->value);
}

if ($node instanceof Node\Scalar\DNumber) {
return new LiteralFloatType($node->value);
}

return new UnknownType('Cannot get type from scalar');
}
}
2 changes: 2 additions & 0 deletions src/Infer/TypeInferer.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function __construct(
private FileNameResolver $nameResolver,
private ?Scope $scope = null,
array $extensions = [],
array $handlers = [],
) {
$this->handlers = [
new FunctionLikeHandler(),
Expand All @@ -58,6 +59,7 @@ public function __construct(
fn ($ext) => $ext instanceof ExpressionExceptionExtension,
))),
new PhpDocHandler(),
...$handlers,
];
}

Expand Down
8 changes: 6 additions & 2 deletions src/Scramble.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ public static function registerUiRoute(string $path, string $api = 'default'): R
{
$config = static::getGeneratorConfig($api);

return RouteFacade::get($path, function (Generator $generator) use ($config) {
return RouteFacade::get($path, function (Generator $generator) use ($api) {
$config = static::getGeneratorConfig($api);

return view('scramble::docs', [
'spec' => $generator($config),
'config' => $config,
Expand All @@ -102,7 +104,9 @@ public static function registerJsonSpecificationRoute(string $path, string $api
{
$config = static::getGeneratorConfig($api);

return RouteFacade::get($path, function (Generator $generator) use ($config) {
return RouteFacade::get($path, function (Generator $generator) use ($api) {
$config = static::getGeneratorConfig($api);

return response()->json($generator($config), options: JSON_PRETTY_PRINT);
})
->middleware($config->get('middleware', [RestrictedDocsAccess::class]));
Expand Down
8 changes: 7 additions & 1 deletion src/ScrambleServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public function configurePackage(Package $package): void
));

return new TypeTransformer(
$this->app->make(Infer::class),
app()->make(Infer::class),
new Components,
array_merge($typesToSchemaExtensions, [
EnumToSchema::class,
Expand Down Expand Up @@ -164,5 +164,11 @@ public function bootingPackage()
Scramble::registerApi('default', config('scramble'))
->routes(Scramble::$routeResolver)
->afterOpenApiGenerated(Scramble::$openApiExtender);

$this->app->booted(function () {
Scramble::getGeneratorConfig('default')
->routes(Scramble::$routeResolver)
->afterOpenApiGenerated(Scramble::$openApiExtender);
});
}
}
22 changes: 21 additions & 1 deletion src/Support/Generator/Parameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@

class Parameter
{
use WithAttributes;

public string $name;

/**
* Possible values are "query", "header", "path" or "cookie".
*
* @var "query"|"header"|"path"|"cookie".
*/
public string $in;

Expand All @@ -18,6 +22,9 @@ class Parameter
/** @var array|scalar|null|MissingExample */
public $example;

/** @var array|scalar|null|MissingExample */
public $default;

public bool $deprecated = false;

public bool $allowEmptyValue = false;
Expand All @@ -28,7 +35,9 @@ public function __construct(string $name, string $in)
{
$this->name = $name;
$this->in = $in;

$this->example = new MissingExample;
$this->default = new MissingExample;

if ($this->in === 'path') {
$this->required = true;
Expand All @@ -55,7 +64,11 @@ public function toArray(): array
$result['schema'] = $this->schema->toArray();
}

return array_merge($result, $this->example instanceof MissingExample ? [] : ['example' => $this->example]);
return array_merge(
$result,
$this->example instanceof MissingExample ? [] : ['example' => $this->example],
$this->default instanceof MissingExample ? [] : ['default' => $this->default],
);
}

public function required(bool $required)
Expand All @@ -79,6 +92,13 @@ public function setSchema(?Schema $schema): self
return $this;
}

public function default($default)
{
$this->default = $default;

return $this;
}

public function description(string $description)
{
$this->description = $description;
Expand Down
Loading

0 comments on commit f69af67

Please sign in to comment.