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

I added support for members and default on attributes #61

Open
wants to merge 6 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
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
language: php

dist: trusty

php:
- 5.5.9
- 5.5
Expand Down
10 changes: 10 additions & 0 deletions src/Annotation/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,18 @@ class Attribute
*/
public $description;

/**
* @var string
*/
public $default;

/**
* @var mixed
*/
public $sample;

/**
* @array<Member>
*/
public $members;
}
264 changes: 255 additions & 9 deletions src/Blueprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
use Illuminate\Filesystem\Filesystem;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
use Dingo\Blueprint\Annotation\Attributes;
use Dingo\Blueprint\Annotation\Attribute;
use Dingo\Blueprint\Annotation\Member;
use Dingo\Blueprint\Annotation\Parameters;
use Dingo\Blueprint\Annotation\Parameter;

class Blueprint
{
Expand Down Expand Up @@ -100,12 +105,12 @@ public function generate(Collection $controllers, $name, $version, $includePath
}

if ($annotations = $this->reader->getMethodAnnotations($method)) {
if (! $actions->contains($method)) {
if (!$actions->contains($method)) {
$annotations = $this->fillAnnotations($annotations, $method);
$actions->push(new Action($method, new Collection($annotations)));
}
}
}

$annotations = new Collection($this->reader->getClassAnnotations($controller));

return new Resource($controller->getName(), $controller, $annotations, $actions);
Expand All @@ -118,6 +123,217 @@ public function generate(Collection $controllers, $name, $version, $includePath
return $contents;
}

/**
* Add the parameters and attributes objects to annotations if needed
*
* @param array $annotations the annotations to fill
* @param object $method the method we are working on
*
* @return array
*/
private function fillAnnotations(array $annotations, $method)
{
$controllerName = substr($method->class, strrpos($method->class, '\\'));
if ($controllerName != '\\OAuthController') {
$rulesClass = 'App\\Validators\\Rules' . $controllerName;
$parameters = $this->extractParams($this->getMethodUri($annotations));

$rules = $rulesClass::$rules[$method->name];
if (count($rules)) {
$annotations[] = $this->getAttributes($rules, $parameters);
$annotations[] = $this->getParameters($rules, $parameters);
}
}
return $annotations;
}

/**
* Get the URI of a method from it's annotations
*
* @param array $annotations
*
* @return string
*/
private function getMethodUri(array $annotations)
{
return array_first($annotations, function ($key, $annotation) {
$type = 'Dingo\\Blueprint\\Annotation\\Method\\Method';
return is_object($annotation) ? $annotation instanceof $type : $key instanceof $type;
})->uri;
}

/**
* Gets a list of all parameters name from mutiple arrays extracted from the uri
*
* @param array $uriParams
*
* @return array
*/
private function extractParams($uri) {
preg_match_all("/{(.*?)}/", $uri, $matches);
return $this->parseParams($matches[1]);
}

/**
* Parse the params as they are in the uri into correct array
*
* @param array $uriParams
*
* @return array
*/
private function parseParams($uriParams)
{
$params = [];

foreach ($uriParams as $uriParam) {
$uriParam = ltrim($uriParam, '?');
$uriParamExploded = explode(',', $uriParam);
foreach ($uriParamExploded as $param) {
$params[] = $param;
}
}
return $params;
}

/**
* Transforms a laravel validation array in Attribute for dingo blueprint
*
* @param array $rules
*
* @return array
*/
private function getAttributes($rules, $parameters)
{
$attributes = new Attributes();

reset($rules);
do {
$identifier = key($rules);
if (in_array($identifier, $parameters)) {
continue;
}
$attribute = $this->parseInfos(new Attribute(), $rules);
$attribute->identifier = $identifier;

$attributes->value[] = $attribute;
} while (next($rules));
return $attributes->value ? $attributes : null;
}

/**
* Transforms a laravel validation array in Parameter for dingo blueprint
*
* @param array $rules
*
* @return array
*/
private function getParameters($rules, $params)
{
$parameters = new Parameters();

reset($rules);
do {
$identifier = key($rules);
if (!in_array($identifier, $params)) {
continue;
}
$parameter = $this->parseInfos(new Parameter(), $rules);
$parameter->identifier = $identifier;

$parameters->value[] = $parameter;
} while (next($rules));
return $parameters->value ? $parameters : null;
}

/**
* Determine if an array in a laravel validator is an array or an object in apiary
*
* @param array $rules
*
* @return string object or array
*/
private function findArrayType(array $rules)
{
$key = key($rules);
$key2 = false;
if (next($rules)) {
$key2 = key($rules);
}
$key3 = false;
if (next($rules)) {
$key3 = key($rules);
}
$toMatch = '/^' . preg_quote($key) . '\.[a-zA-Z0-9]*$/';
if ($key2 && preg_match($toMatch, $key2) === 1 && $key3 && preg_match($toMatch, $key2)) {
$type = 'object';
} else {
$type = 'array';
}
return $type;
}

/**
* Parse the validation array to fill a parameter or an attribute
*
* @param Parameter|Attribute $toFill
* @param array $rules
*
* @return Parameter|Attribute
*/
private function parseInfos($toFill, array $rules)
{
$infos = current($rules);
$toFill->description = $infos['description'];

$propertiesExploded = explode('|', $infos['rules']);
foreach ($propertiesExploded as $property) {
switch ($property) {
case 'numeric':
case 'integer':
$toFill->type = 'number';
continue 2;
case 'array':
$toFill->type = $this->findArrayType($rules);
continue 2;
case 'present':
case 'required':
$toFill->required = true;
continue 2;
}
$tofill = $this->parseComplexProperties($toFill, $property);
}
return $toFill;
}

/**
* Parse the property to find enum or defaults
*
* @param Parameter|Attribute $toFill
* @param string $property
*
* @return Parameter|Attribute
*/
private function parseComplexProperties($toFill, $property)
{
$propertyExploded = explode(':', $property);
if (count($propertyExploded) > 1) {
switch ($propertyExploded[0]) {
case 'default':
$toFill->default = $propertyExploded[1];
break;
case 'in':
$members = explode(',', $propertyExploded[1]);
foreach ($members as $identifier) {
$member = new Member();
$member->identifier = $identifier;
$toFill->members[] = $member;
}
break;
}
}
return $toFill;
}

/**
* Generate the documentation contents from the resources collection.
*
Expand All @@ -131,6 +347,7 @@ protected function generateContentsFromResources(Collection $resources, $name)
$contents = '';

$contents .= $this->getFormat();
$contents .= $this->getHost();
$contents .= $this->line(2);
$contents .= sprintf('# %s', $name);
$contents .= $this->line(2);
Expand Down Expand Up @@ -209,21 +426,36 @@ protected function appendAttributes(&$contents, Collection $attributes, $indent
$this->appendSection($contents, 'Attributes', $indent);

$attributes->each(function ($attribute) use (&$contents, $indent) {
$explodedIdentifier = explode('.', $attribute->identifier);
$indent += count($explodedIdentifier);
$contents .= $this->line();
$contents .= $this->tab(1 + $indent);
$contents .= sprintf('+ %s', $attribute->identifier);
$contents .= $this->tab($indent);
$identifier = $explodedIdentifier[count($explodedIdentifier) - 1];
$contents .= sprintf('+ %s', $identifier == '*' ? '' : $identifier);

if ($attribute->sample) {
$contents .= sprintf(': %s', $attribute->sample);
}

$contents .= sprintf(
' (%s, %s) - %s',
$attribute->type,
$attribute->members ? sprintf('enum[%s]', $attribute->type) : $attribute->type,
$attribute->required ? 'required' : 'optional',
$attribute->description
);

if (isset($attribute->default)) {
$this->appendSection($contents, sprintf('Default: %s', $attribute->default), $indent + 1, 1);
}

if (isset($attribute->members)) {
$this->appendSection($contents, 'Members', $indent + 1, 1);
foreach ($attribute->members as $member) {
$this->appendSection($contents, sprintf('`%s` - %s', $member->identifier, $member->description), $indent + 2, 1);
}
}
});

}

/**
Expand All @@ -241,10 +473,14 @@ protected function appendParameters(&$contents, Collection $parameters)
$parameters->each(function ($parameter) use (&$contents) {
$contents .= $this->line();
$contents .= $this->tab();
$contents .= sprintf('+ %s', $parameter->identifier);

if ($parameter->example) {
$contents .= sprintf(': %s', $parameter->example);
}

$contents .= sprintf(
'+ %s:%s (%s, %s) - %s',
$parameter->identifier,
$parameter->example ? " `{$parameter->example}`" : '',
' (%s, %s) - %s',
$parameter->members ? sprintf('enum[%s]', $parameter->type) : $parameter->type,
$parameter->required ? 'required' : 'optional',
$parameter->description
Expand Down Expand Up @@ -312,7 +548,10 @@ protected function appendRequest(&$contents, $request, Resource $resource)

$contents .= ' ('.$request->contentType.')';

if (! empty($request->headers) || $resource->hasRequestHeaders()) {
if (!isset($request->headers['Authorization'])) {
$request->headers['Authorization'] = 'OAuth: oauth_consumer_key={consumer_key},oauth_signature={consumer_secret}&{user_secret},oauth_signature_method=PLAINTEXT,oauth_nonce={random_string},oauth_timestamp={current_timestamp},oauth_token={user_token}';
}
if (!empty($request->headers) || $resource->hasRequestHeaders()) {
$this->appendHeaders($contents, array_merge($resource->getRequestHeaders(), $request->headers));
}

Expand Down Expand Up @@ -454,4 +693,11 @@ protected function getFormat()
{
return 'FORMAT: 1A';
}

private function getHost()
{
if (class_exists('\Illuminate\Config\Repository')) {
return $this->line(1) . 'HOST: https://' . app('config')->get('api.domain') . '/' . app('config')->get('api.prefix');
}
}
}