Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
sotdan authored Sep 27, 2024
2 parents 141554a + 8a6c644 commit 3c68b5c
Show file tree
Hide file tree
Showing 29 changed files with 557 additions and 71 deletions.
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Removed

# 4.37.2 (30 August 2024)
## Fixed
- Stop response fields from overflowing to the dark box zone [#868](https://github.com/knuckleswtf/scribe/pull/868)
- Don't ignore comments for validator parameters with non string/array (e.g. conditional) rule lists [#880](https://github.com/knuckleswtf/scribe/pull/880)
- Allow custom output path for static and external_static instead of only static [#884](https://github.com/knuckleswtf/scribe/pull/884)


# 4.37.1 (11 July 2024)
## Fixed
- Multipart file upload in `elements` theme [#864](https://github.com/knuckleswtf/scribe/pull/864)
- Properly set multiple responses in OpenAPI spec with the same status code [#863](https://github.com/knuckleswtf/scribe/pull/863)



# 4.37.0 (17 June 2024)
## Added
- Support multiple responses in OpenAPI spec using oneOf [#739](https://github.com/knuckleswtf/scribe/pull/739)


# 4.36.0 (27 May 2024)
## Added
- Add `afterResponseCall` hook [#847](https://github.com/knuckleswtf/scribe/pull/847)

## Fixed
- Unescape tryItOutBaseURL [09b49b582](https://github.com/knuckleswtf/scribe/commit/09b49b5829647597825b2cc7162382e926d53f90)
- Ignore `external.html_attributes` for upgrades [f56a48014](https://github.com/knuckleswtf/scribe/commit/f56a480140d25ada8a441f69db9a6a14b5f0dcd1)
- Fix missing title and logo in `elements` theme [#844](https://github.com/knuckleswtf/scribe/pull/844)


# 4.35.0 (26 March 2024)
## Modified
- Allow examples to be shown in response fields [#825](https://github.com/knuckleswtf/scribe/pull/825)

## Fixed
- Try It Out: send numbers in JSON as float, not strings [#830](https://github.com/knuckleswtf/scribe/pull/830)
- Fix "No such file or directory" error [#829](https://github.com/knuckleswtf/scribe/pull/829)
- Fix translating rules with translation engines that don't return arrays [#826](https://github.com/knuckleswtf/scribe/pull/826)

# 4.34.0 (15 March 2024)
## Added
- Laravel 11 compatibility [#812](https://github.com/knuckleswtf/scribe/pull/812)

## Modified
- Instantiate some classes via service container for easier overriding. [#822](https://github.com/knuckleswtf/scribe/pull/822)

# 4.33.0 (29 February 2024)
## Fixed
- List enums for array items in OpenAPI spec [#818](https://github.com/knuckleswtf/scribe/pull/818)
Expand Down
3 changes: 3 additions & 0 deletions camel/Extraction/ResponseField.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@ class ResponseField extends BaseDTO
/** @var boolean */
public $required;

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

public array $enumValues = [];
}
8 changes: 7 additions & 1 deletion config/scribe.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,13 @@
Strategies\Responses\UseResponseFileTag::class,
[
Strategies\Responses\ResponseCalls::class,
['only' => ['GET *']]
[
'only' => ['GET *'],
// Disable debug mode when generating response calls to avoid error stack traces in responses
'config' => [
'app.debug' => false,
],
]
]
],
'responseFields' => [
Expand Down
21 changes: 7 additions & 14 deletions resources/css/theme-default.style.css
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ html {
.content>p,
.content>table,
.content>ul,
.content>div,
.content>form>aside,
.content>form>details,
.content>form>h1,
Expand Down Expand Up @@ -893,6 +894,11 @@ html {
text-shadow: 0 1px 2px rgba(0, 0, 0, .4)
}

.content blockquote pre.sf-dump,
.content pre pre.sf-dump {
width: 100%;
}

.content .annotation {
background-color: #292929;
color: #fff;
Expand Down Expand Up @@ -961,20 +967,6 @@ html {
.page-wrapper .lang-selector {
display: none
}
.content aside,
.content dl,
.content h1,
.content h2,
.content h3,
.content h4,
.content h5,
.content h6,
.content ol,
.content p,
.content table,
.content ul {
margin-right: 0
}
.content>aside,
.content>details,
.content>dl,
Expand All @@ -988,6 +980,7 @@ html {
.content>p,
.content>table,
.content>ul,
.content>div,
.content>form>aside,
.content>form>details,
.content>form>h1,
Expand Down
19 changes: 18 additions & 1 deletion resources/js/tryitout.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,30 @@ function handleResponse(endpointId, response, status, headers) {

const responseContentEl = document.querySelector('#execution-response-content-' + endpointId);

// Check if the response contains Laravel's dd() default dump output
const isLaravelDump = response.includes('Sfdump');

// If it's a Laravel dd() dump, use innerHTML to render it safely
if (isLaravelDump) {
responseContentEl.innerHTML = response === '' ? responseContentEl.dataset.emptyResponseText : response;
} else {
// Otherwise, stick to textContent for regular responses
responseContentEl.textContent = response === '' ? responseContentEl.dataset.emptyResponseText : response;
}

// Prettify it if it's JSON
let isJson = false;
try {
const jsonParsed = JSON.parse(response);
if (jsonParsed !== null) {
isJson = true;
response = JSON.stringify(jsonParsed, null, 4);
responseContentEl.textContent = response;
}
} catch (e) {

}
responseContentEl.textContent = response === '' ? responseContentEl.dataset.emptyResponseText : response;

isJson && window.hljs.highlightElement(responseContentEl);
const statusEl = document.querySelector('#execution-response-status-' + endpointId);
statusEl.textContent = ` (${status})`;
Expand Down Expand Up @@ -194,6 +206,11 @@ async function executeTryOut(endpointId, form) {
const bodyParameters = form.querySelectorAll('input[data-component=body]');
bodyParameters.forEach(el => {
let value = el.value;

if (el.type === 'number' && typeof value === 'string') {
value = parseFloat(value);
}

if (el.type === 'file' && el.files[0]) {
setter(el.name, el.files[0]);
return;
Expand Down
9 changes: 8 additions & 1 deletion resources/views/external/elements.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Elements in HTML</title>
<title>{!! $metadata['title'] !!}</title>
<!-- Embed elements Elements via Web Component -->
<script src="https://unpkg.com/@stoplight/elements/web-components.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css">
<style>
body {
height: 100vh;
}
</style>
</head>
<body>

Expand All @@ -20,7 +25,9 @@
router="hash"
layout="sidebar"
hideTryIt="{!! ($tryItOut['enabled'] ?? true) ? '' : 'true'!!}"
@if(!empty($metadata['logo']))
logo="{!! $metadata['logo'] !!}"
@endif
/>

</body>
Expand Down
6 changes: 3 additions & 3 deletions resources/views/themes/default/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@

@if($tryItOut['enabled'] ?? true)
<script>
var tryItOutBaseUrl = "{{ $tryItOut['base_url'] ?? config('app.url') }}";
var useCsrf = Boolean({{ $tryItOut['use_csrf'] ?? null }});
var csrfUrl = "{{ $tryItOut['csrf_url'] ?? null }}";
var tryItOutBaseUrl = "{!! $tryItOut['base_url'] ?? config('app.url') !!}";
var useCsrf = Boolean({!! $tryItOut['use_csrf'] ?? null !!});
var csrfUrl = "{!! $tryItOut['csrf_url'] ?? null !!}";
</script>
<script src="{{ u::getVersionedAsset($assetPathPrefix.'js/tryitout.js') }}"></script>
@endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ class="svg-inline--fa fa-chevron-right fa-fw fa-sm sl-icon" role="img"
@endif
</div>
@endif
@if(!$hasChildren && !is_null($example) && $example != '')
@if(!$hasChildren && !is_null($example) && $example !== '')
<div class="sl-stack sl-stack--horizontal sl-stack--2 sl-flex sl-flex-row sl-items-baseline sl-text-muted">
<span>Example:</span> <!-- <span> important for spacing -->
<div class="sl-flex sl-flex-1 sl-flex-wrap" style="gap: 4px;">
<div class="sl-max-w-full sl-break-all sl-px-1 sl-bg-canvas-tint sl-text-muted sl-rounded sl-border">
{{ is_array($example) ? json_encode($example) : $example }}
{{ is_array($example) || is_bool($example) ? json_encode($example) : $example }}
</div>
</div>
</div>
Expand Down
8 changes: 8 additions & 0 deletions resources/views/themes/elements/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ function tryItOut(btnElement) {
});
}
// content type has to be unset otherwise file upload won't work
if (form.dataset.hasfiles === "1") {
delete headers['Content-Type'];
}
return preflightPromise.then(() => makeAPICall(method, path, body, query, headers, endpointId))
.then(([responseStatus, statusText, responseContent, responseHeaders]) => {
responsePanel.hidden = false;
Expand All @@ -132,6 +137,9 @@ function tryItOut(btnElement) {
}
} catch (e) {}
// Replace HTML entities
responseContent = responseContent.replace(/[<>&]/g, (i) => '&#' + i.charCodeAt(0) + ';');
contentEl.innerHTML = responseContent;
isJson && window.hljs.highlightElement(contentEl);
})
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/GenerateDocumentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function handle(RouteMatcherInterface $routeMatcher, GroupedEndpointsFact
$this->writeExampleCustomEndpoint();
}

$writer = new Writer($this->docConfig, $this->paths);
$writer = app(Writer::class, ['config' => $this->docConfig, 'paths' => $this->paths]);
$writer->writeDocs($groupedEndpoints);

$this->upgradeConfigFileIfNeeded();
Expand Down Expand Up @@ -178,7 +178,7 @@ protected function upgradeConfigFileIfNeeded(): void
)
->dontTouch(
'routes', 'example_languages', 'database_connections_to_transact', 'strategies', 'laravel.middleware',
'postman.overrides', 'openapi.overrides', 'groups', 'examples.models_source'
'postman.overrides', 'openapi.overrides', 'groups', 'examples.models_source', 'external.html_attributes'
);
$changes = $upgrader->dryRun();
if (!empty($changes)) {
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/Upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function handle(): void

$upgrader = Upgrader::ofConfigFile("config/$this->configName.php", __DIR__ . '/../../config/scribe.php')
->dontTouch('routes', 'laravel.middleware', 'postman.overrides', 'openapi.overrides',
'example_languages', 'database_connections_to_transact', 'strategies', 'examples.models_source')
'example_languages', 'database_connections_to_transact', 'strategies', 'examples.models_source', 'external.html_attributes')
->move('default_group', 'groups.default')
->move('faker_seed', 'examples.faker_seed');

Expand Down
9 changes: 4 additions & 5 deletions src/Extracting/DatabaseTransactionHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ private function connectionsToTransact()

private function startDbTransaction()
{
$database = app('db');

foreach ($this->connectionsToTransact() as $connection) {
$database ??= app('db');

$driver = $database->connection($connection);

if (self::driverSupportsTransactions($driver)) {
Expand All @@ -30,7 +30,6 @@ private function startDbTransaction()
" If you aren't using this database, remove it from the `database_connections_to_transact` config array."
);
}
continue;
} else {
$driverClassName = get_class($driver);
throw DatabaseTransactionsNotSupported::create($connection, $driverClassName);
Expand All @@ -43,9 +42,9 @@ private function startDbTransaction()
*/
private function endDbTransaction()
{
$database = app('db');

foreach ($this->connectionsToTransact() as $connection) {
$database ??= app('db');

$driver = $database->connection($connection);
try {
$driver->rollback();
Expand Down
25 changes: 13 additions & 12 deletions src/Extracting/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,23 +215,24 @@ protected function iterateThroughStrategies(
}

$settings = self::transformOldRouteRulesIntoNewSettings($stage, $rulesToApply, $strategyClass, $settings);
$routesToSkip = $settings["except"] ?? [];
$routesToInclude = $settings["only"] ?? [];

if (!empty($routesToSkip)) {
if (RoutePatternMatcher::matches($endpointData->route, $routesToSkip)) {
continue;
}
} elseif (!empty($routesToInclude)) {
if (!RoutePatternMatcher::matches($endpointData->route, $routesToInclude)) {
continue;
}
}
} else {
$strategyClass = $strategyClassOrTuple;
$settings = self::transformOldRouteRulesIntoNewSettings($stage, $rulesToApply, $strategyClass);
}

$routesToSkip = $settings["except"] ?? [];
$routesToInclude = $settings["only"] ?? [];

if (!empty($routesToSkip)) {
if (RoutePatternMatcher::matches($endpointData->route, $routesToSkip)) {
continue;
}
} elseif (!empty($routesToInclude)) {
if (!RoutePatternMatcher::matches($endpointData->route, $routesToInclude)) {
continue;
}
}

$strategy = new $strategyClass($this->config);
$results = $strategy($endpointData, $settings);
if (is_array($results)) {
Expand Down
27 changes: 21 additions & 6 deletions src/Extracting/ParsesValidationRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,9 @@ protected function parseRule($rule, array &$parameterData, bool $independentOnly
case 'different':
$parameterData['description'] .= " The value and <code>{$arguments[0]}</code> must be different.";
break;

case 'exists':
$parameterData['description'] .= " The <code>{$arguments[1]}</code> of an existing record in the {$arguments[0]} table.";
break;
default:
// Other rules not supported
break;
Expand Down Expand Up @@ -636,8 +638,11 @@ public function convertGenericArrayType(array $parameters): array
// 2. If `users.<name>` exists, `users` is an `object`
// 3. Otherwise, default to `object`
// Important: We're iterating in reverse, to ensure we set child items before parent items
// (assuming the user specified parents first, which is the more common thing)
if ($childKey = Arr::first($allKeys, fn($key) => Str::startsWith($key, "$name.*"))) {
// (assuming the user specified parents first, which is the more common thing)y
if(Arr::first($allKeys, fn($key) => Str::startsWith($key, "$name.*."))) {
$details['type'] = 'object[]';
unset($details['setter']);
} else if ($childKey = Arr::first($allKeys, fn($key) => Str::startsWith($key, "$name.*"))) {
$childType = ($converted[$childKey] ?? $parameters[$childKey])['type'];
$details['type'] = "{$childType}[]";
} else { // `array` types default to `object` if no subtype is specified
Expand Down Expand Up @@ -778,11 +783,21 @@ protected function getDescription(string $rule, array $arguments = [], $baseType
return "Must match the regex {$arguments[':regex']}.";
}

$description = trans("validation.{$rule}");
// For rules that can apply to multiple types (eg 'max' rule), Laravel returns an array of possible messages
$translationString = "validation.{$rule}";
$description = trans($translationString);

// For rules that can apply to multiple types (eg 'max' rule), There is an array of possible messages
// 'numeric' => 'The :attribute must not be greater than :max'
// 'file' => 'The :attribute must have a size less than :max kilobytes'
if (is_array($description)) {
// Depending on the translation engine, trans may return the array, or it will fail to translate the string
// and will need to be called with the baseType appended.
if ($description === $translationString) {
$translationString = "{$translationString}.{$baseType}";
$translated = trans($translationString);
if ($translated !== $translationString) {
$description = $translated;
}
} elseif (is_array($description)) {
$description = $description[$baseType];
}

Expand Down
Loading

0 comments on commit 3c68b5c

Please sign in to comment.