open api spec
This commit is contained in:
@@ -69,4 +69,9 @@ class FieldType
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const DECIMAL = 'decimal';
|
||||
|
||||
/**
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const URL_MULTIPLE = 'urlMultiple';
|
||||
}
|
||||
|
||||
@@ -103,4 +103,39 @@ class FieldParam
|
||||
* Foreign field.
|
||||
*/
|
||||
public const FIELD = 'field';
|
||||
|
||||
/**
|
||||
* Required.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const REQUIRED = 'required';
|
||||
|
||||
/**
|
||||
* Disabled.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const DISABLED = 'disabled';
|
||||
|
||||
/**
|
||||
* Utility. For internal purposes.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const UTILITY = 'utility';
|
||||
|
||||
/**
|
||||
* Min value.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const MIN = 'min';
|
||||
|
||||
/**
|
||||
* Max value.
|
||||
*
|
||||
* @since 9.3.0
|
||||
*/
|
||||
public const MAX = 'max';
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@
|
||||
"AddressCountry": "Address Country",
|
||||
"AppSecret": "App Secret",
|
||||
"OAuthProvider": "OAuth Provider",
|
||||
"OAuthAccount": "OAuth Account"
|
||||
"OAuthAccount": "OAuth Account",
|
||||
"OpenApi": "OpenAPI"
|
||||
},
|
||||
"scopeNamesPlural": {
|
||||
"Note": "Notes",
|
||||
@@ -125,7 +126,8 @@
|
||||
"AddressCountry": "Address Countries",
|
||||
"AppSecret": "App Secrets",
|
||||
"OAuthProvider": "OAuth Providers",
|
||||
"OAuthAccount": "OAuth Accounts"
|
||||
"OAuthAccount": "OAuth Accounts",
|
||||
"OpenApi": "OpenAPI"
|
||||
},
|
||||
"labels": {
|
||||
"Previous Page": "Previous Page",
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
"customizationInlineEditDisabledDisabled": true,
|
||||
"customizationDefaultView": "views/admin/field-manager/fields/currency-default",
|
||||
"customizationTooltipTextDisabled": true,
|
||||
"maxLength": 3
|
||||
"maxLength": 3,
|
||||
"apiSpecDisabled": true
|
||||
},
|
||||
"converted": {
|
||||
"type": "currencyConverted",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"acl": "boolean"
|
||||
}
|
||||
@@ -500,6 +500,11 @@
|
||||
"route": "/OAuth/:id/connection",
|
||||
"actionClassName": "Espo\\Tools\\OAuth\\Api\\DeleteConnection"
|
||||
},
|
||||
{
|
||||
"method": "get",
|
||||
"route": "/OpenApi",
|
||||
"actionClassName": "Espo\\Tools\\OpenApi\\Api\\GetSpec"
|
||||
},
|
||||
{
|
||||
"route": "/:controller/:id",
|
||||
"method": "get",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM – Open Source CRM application.
|
||||
* Copyright (C) 2014-2025 EspoCRM, Inc.
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU Affero General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Tools\OpenApi\Api;
|
||||
|
||||
use Espo\Core\Acl;
|
||||
use Espo\Core\Api\Action;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Api\ResponseComposer;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Tools\OpenApi\ProviderFactory;
|
||||
|
||||
/**
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class GetSpec implements Action
|
||||
{
|
||||
private const string SCOPE = 'OpenApi';
|
||||
|
||||
public function __construct(
|
||||
private Acl $acl,
|
||||
private ProviderFactory $providerFactory,
|
||||
) {}
|
||||
|
||||
public function process(Request $request): Response
|
||||
{
|
||||
$this->checkAccess();
|
||||
|
||||
$provider = $this->providerFactory->create();
|
||||
|
||||
$spec = $provider->get();
|
||||
|
||||
return ResponseComposer::empty()
|
||||
->writeBody($spec)
|
||||
->setHeader('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Forbidden
|
||||
*/
|
||||
private function checkAccess(): void
|
||||
{
|
||||
if (!$this->acl->checkScope(self::SCOPE)) {
|
||||
throw new Forbidden("No access to OpenApi scope.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi;
|
||||
|
||||
/**
|
||||
* Not stable yet. May change.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface FieldSchemaBuilder
|
||||
{
|
||||
public function build(string $entityType, string $field): FieldSchemaResult;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi;
|
||||
|
||||
use Espo\Core\InjectableFactory;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\NoSupport;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\EnumType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\MultiEnumType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\PhoneType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\TextType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\VarcharType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\BoolType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\IntType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\FloatType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\DecimalType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\AutoincrementType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\CurrencyType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\CurrencyConvertedType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\NumberType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\DateType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\DatetimeType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\DatetimeOptionalType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\ForeignType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\EmailType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\LinkType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\LinkParentType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\LinkMultipleType;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilders\IdType;
|
||||
|
||||
class FieldSchemaBuilderFactory
|
||||
{
|
||||
/** @var array<string, class-string<FieldSchemaBuilder>> */
|
||||
private array $map = [
|
||||
FieldType::VARCHAR => VarcharType::class,
|
||||
FieldType::URL => VarcharType::class,
|
||||
FieldType::ENUM => EnumType::class,
|
||||
FieldType::TEXT => TextType::class,
|
||||
FieldType::WYSIWYG => TextType::class,
|
||||
FieldType::NUMBER => NumberType::class,
|
||||
FieldType::BOOL => BoolType::class,
|
||||
FieldType::INT => IntType::class,
|
||||
FieldType::FLOAT => FloatType::class,
|
||||
FieldType::DECIMAL => DecimalType::class,
|
||||
FieldType::CURRENCY => CurrencyType::class,
|
||||
FieldType::AUTOINCREMENT => AutoincrementType::class,
|
||||
FieldType::CURRENCY_CONVERTED => CurrencyConvertedType::class,
|
||||
FieldType::FOREIGN => ForeignType::class,
|
||||
FieldType::EMAIL => EmailType::class,
|
||||
FieldType::PHONE => PhoneType::class,
|
||||
FieldType::DATE => DateType::class,
|
||||
FieldType::DATETIME => DatetimeType::class,
|
||||
FieldType::DATETIME_OPTIONAL => DatetimeOptionalType::class,
|
||||
FieldType::MULTI_ENUM => MultiEnumType::class,
|
||||
FieldType::ARRAY => MultiEnumType::class,
|
||||
FieldType::CHECKLIST => MultiEnumType::class,
|
||||
FieldType::URL_MULTIPLE => MultiEnumType::class,
|
||||
FieldType::LINK => LinkType::class,
|
||||
FieldType::LINK_ONE => LinkType::class,
|
||||
FieldType::FILE => LinkType::class,
|
||||
FieldType::IMAGE => LinkType::class,
|
||||
FieldType::LINK_PARENT => LinkParentType::class,
|
||||
FieldType::LINK_MULTIPLE => LinkMultipleType::class,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private InjectableFactory $injectableFactory,
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function create(string $entityType, string $field): FieldSchemaBuilder
|
||||
{
|
||||
$className = $this->getClassName($field, $entityType);
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<FieldSchemaBuilder>
|
||||
*/
|
||||
private function getClassName(string $field, string $entityType): string
|
||||
{
|
||||
if ($field === Attribute::ID) {
|
||||
return IdType::class;
|
||||
}
|
||||
|
||||
$fieldDefs = $this->defs
|
||||
->getEntity($entityType)
|
||||
->getField($field);
|
||||
|
||||
$type = $fieldDefs->getType();
|
||||
|
||||
return $this->map[$type] ?? NoSupport::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class AutoincrementType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$schema = (object) [
|
||||
'type' => Type::INTEGER,
|
||||
'readOnly' => true,
|
||||
'description' => 'An auto-increment number.',
|
||||
];
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class BoolType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [
|
||||
'type' => Type::BOOLEAN,
|
||||
'readOnly' => $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false,
|
||||
];
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class CurrencyConvertedType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::NUMBER;
|
||||
$schema->readOnly = true;
|
||||
$schema->description = 'A currency amount converted to the default currency.';
|
||||
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Core\Currency\ConfigDataProvider;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class CurrencyType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
private ConfigDataProvider $configDataProvider,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::DECIMAL)) {
|
||||
$schema->type = Type::STRING;
|
||||
$schema->pattern = '^-?\d+(\.\d+)?$';
|
||||
$schema->description = 'A numeric string';
|
||||
} else {
|
||||
$schema->type = Type::NUMBER;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MIN) !== null) {
|
||||
$schema->minimum = $fieldDefs->getParam(FieldParam::MIN);
|
||||
}
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MAX) !== null) {
|
||||
$schema->maximum = $fieldDefs->getParam(FieldParam::MAX);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A currency amount.';
|
||||
|
||||
$codeSchema = (object) [];
|
||||
$codeSchema->type = Type::STRING;
|
||||
$codeSchema->enum = $this->configDataProvider->getCurrencyList();
|
||||
$codeSchema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$codeSchema->type = [
|
||||
$codeSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$codeSchema->description = 'A code.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
$required[] = $field . 'Currency';
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
$field . 'Currency' => get_object_vars($codeSchema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class DateType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
$schema->format = 'date';
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A date.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class DatetimeOptionalType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->pattern = '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$';
|
||||
$schema->examples = ['2026-11-29 12:34:56'];
|
||||
$schema->description = 'A timestamp in UTC.';
|
||||
|
||||
$schemaDate = (object) [];
|
||||
$schemaDate->type = Type::STRING;
|
||||
$schemaDate->format = 'date';
|
||||
$schemaDate->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
$schemaDate->type = [
|
||||
$schemaDate->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$schema->description = "Specified if the '$field' field is all-day.";
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
$field . 'Date' => get_object_vars($schemaDate),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class DatetimeType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->pattern = '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$';
|
||||
$schema->examples = ['2026-11-29 12:34:56'];
|
||||
$schema->description = 'A timestamp in UTC.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class DecimalType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
$schema->pattern = '^-?\d+(\.\d+)?$';
|
||||
$schema->description = 'A numeric string.';
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class EmailType implements FieldSchemaBuilder
|
||||
{
|
||||
const int DEFAULT_MAX_LENGTH = 255;
|
||||
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->maxLength = self::DEFAULT_MAX_LENGTH;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A primary email address.';
|
||||
|
||||
$itemSchema = (object) [];
|
||||
$itemSchema->type = Type::OBJECT;
|
||||
$itemSchema->properties = [
|
||||
'emailAddress' => [
|
||||
'type' => Type::STRING,
|
||||
],
|
||||
'primary' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'optOut' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'invalid' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'lower' => [
|
||||
'type' => Type::STRING,
|
||||
'readOnly' => true,
|
||||
],
|
||||
];
|
||||
$itemSchema->required = [
|
||||
'emailAddress',
|
||||
'primary',
|
||||
];
|
||||
|
||||
$dataSchema = (object) [];
|
||||
$dataSchema->type = Type::ARRAY;
|
||||
$dataSchema->items = get_object_vars($itemSchema);
|
||||
$dataSchema->description = 'Multiple email addresses';
|
||||
|
||||
$dataSchema->type = [
|
||||
$dataSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
$field . 'Data' => get_object_vars($dataSchema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
use Espo\Tools\OpenApi\Util\EnumOptionsProvider;
|
||||
|
||||
class EnumType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
private EnumOptionsProvider $optionsProvider,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
$optionList = $this->optionsProvider->get($fieldDefs);
|
||||
|
||||
if ($optionList) {
|
||||
if (in_array('', $optionList) && !$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
Type::STRING,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$optionList = array_filter($optionList, fn ($it) => $it !== '');
|
||||
$optionList = array_values($optionList);
|
||||
|
||||
$schema->enum = $optionList;
|
||||
}
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class FloatType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::NUMBER;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MIN) !== null) {
|
||||
$schema->minimum = $fieldDefs->getParam(FieldParam::MIN);
|
||||
}
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MAX) !== null) {
|
||||
$schema->maximum = $fieldDefs->getParam(FieldParam::MAX);
|
||||
}
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class ForeignType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$schema = (object) [];
|
||||
$schema->readOnly = true;
|
||||
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$link = $fieldDefs->getParam(FieldParam::LINK);
|
||||
$foreignField = $fieldDefs->getParam(FieldParam::FIELD);
|
||||
|
||||
$foreignEntityType = $this->defs
|
||||
->getEntity($entityType)
|
||||
->tryGetRelation($link)
|
||||
?->tryGetForeignEntityType();
|
||||
|
||||
if (!$foreignEntityType) {
|
||||
return new FieldSchemaResult([]);
|
||||
}
|
||||
|
||||
$foreignFieldDefs = $this->defs
|
||||
->getEntity($foreignEntityType)
|
||||
->tryGetField($foreignField);
|
||||
|
||||
if (!$foreignFieldDefs) {
|
||||
return new FieldSchemaResult([]);
|
||||
}
|
||||
|
||||
$fieldType = $foreignFieldDefs->getType();
|
||||
|
||||
if (
|
||||
$fieldType === FieldType::ENUM ||
|
||||
$fieldType === FieldType::VARCHAR ||
|
||||
$fieldType === FieldType::TEXT ||
|
||||
$fieldType === FieldType::DATE ||
|
||||
$fieldType === FieldType::DATETIME ||
|
||||
$fieldType === FieldType::EMAIL ||
|
||||
$fieldType === FieldType::PHONE ||
|
||||
$fieldType === FieldType::WYSIWYG ||
|
||||
$fieldType === FieldType::DECIMAL
|
||||
) {
|
||||
$schema->type = Type::STRING;
|
||||
} else if (
|
||||
$foreignEntityType === FieldType::INT
|
||||
) {
|
||||
$schema->type = Type::INTEGER;
|
||||
} else if (
|
||||
$foreignEntityType === FieldType::FLOAT
|
||||
) {
|
||||
$schema->type = Type::NUMBER;
|
||||
} else {
|
||||
return new FieldSchemaResult([]);
|
||||
}
|
||||
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class IdType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->tryGetField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = true;
|
||||
|
||||
$dbType = $fieldDefs?->getParam(Defs\Params\FieldParam::DB_TYPE);
|
||||
|
||||
if ($dbType === Types::BIGINT || $dbType === Types::INTEGER) {
|
||||
$schema->type = Type::INTEGER;
|
||||
}
|
||||
|
||||
$schema->description = 'An ID.';
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class IntType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::INTEGER;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MIN) !== null) {
|
||||
$schema->minimum = $fieldDefs->getParam(FieldParam::MIN);
|
||||
}
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MAX) !== null) {
|
||||
$schema->maximum = $fieldDefs->getParam(FieldParam::MAX);
|
||||
}
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Core\ORM\Type\FieldType;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
use Espo\Tools\OpenApi\Util\EnumOptionsProvider;
|
||||
|
||||
class LinkMultipleType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
private EnumOptionsProvider $enumOptionsProvider,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
$linkDefs = $this->defs->getEntity($entityType)->tryGetRelation($field);
|
||||
|
||||
$idsSchema = (object) [
|
||||
'type' => Type::ARRAY,
|
||||
'items' => [
|
||||
'type' => Type::STRING,
|
||||
],
|
||||
];
|
||||
|
||||
$idsSchema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
$foreignEntityType = null;
|
||||
|
||||
if ($linkDefs && $linkDefs->tryGetForeignEntityType()) {
|
||||
$foreignEntityType = $linkDefs->tryGetForeignEntityType();
|
||||
|
||||
$idsSchema->description = "IDs of records of the $foreignEntityType type.";
|
||||
}
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$idsSchema->type = [
|
||||
$idsSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$namesSchema = (object) [];
|
||||
$namesSchema->type = Type::OBJECT;
|
||||
$namesSchema->additionalProperties = [
|
||||
'type' => Type::STRING,
|
||||
];
|
||||
$namesSchema->readOnly = true;
|
||||
$namesSchema->description = 'An {ID => record name} map.';
|
||||
|
||||
$namesSchema->type = [
|
||||
$namesSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$output = [
|
||||
$field . 'Ids' => get_object_vars($idsSchema),
|
||||
$field . 'Names' => get_object_vars($namesSchema),
|
||||
];
|
||||
|
||||
/** @var ?array<string, string> $columns */
|
||||
$columns = $fieldDefs->getParam('columns');
|
||||
|
||||
if (is_array($columns) && $foreignEntityType) {
|
||||
$columnsSchema = (object) [];
|
||||
$columnsSchema->type = Type::OBJECT;
|
||||
$columnsSchema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
$columnsSchema->description = 'An {ID => object} map. Relationship column values.';
|
||||
|
||||
$columnsSchema->type = [
|
||||
$columnsSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$properties = array_map(function ($columnField) use ($entityType, $foreignEntityType) {
|
||||
return $this->prepareColumnSchema($entityType, $foreignEntityType, $columnField);
|
||||
}, $columns);
|
||||
|
||||
$columnsSchema->additionalProperties = [
|
||||
'type' => Type::OBJECT,
|
||||
'properties' => $properties,
|
||||
];
|
||||
|
||||
$output[$field . 'Columns'] = get_object_vars($columnsSchema);
|
||||
}
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field . 'Ids';
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: $output,
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function prepareColumnSchema(string $entityType, string $foreignEntityType, string $columnField): array
|
||||
{
|
||||
$colSchema = (object) [];
|
||||
|
||||
$fieldDefs = $this->defs->getEntity($foreignEntityType)->tryGetField($columnField);
|
||||
|
||||
if (!$fieldDefs) {
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->tryGetField($columnField);
|
||||
}
|
||||
|
||||
if (!$fieldDefs) {
|
||||
return get_object_vars($colSchema);
|
||||
}
|
||||
|
||||
$optionList = $this->enumOptionsProvider->get($fieldDefs);
|
||||
|
||||
if ($optionList) {
|
||||
$colSchema->type = Type::STRING;
|
||||
|
||||
if (in_array('', $optionList)) {
|
||||
$colSchema->type = [
|
||||
$colSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$optionList = array_filter($optionList, fn($it) => $it !== '');
|
||||
$optionList = array_values($optionList);
|
||||
|
||||
$colSchema->enum = $optionList;
|
||||
|
||||
return get_object_vars($colSchema);
|
||||
}
|
||||
|
||||
if ($fieldDefs->getType() === FieldType::BOOL) {
|
||||
$colSchema->type = Type::BOOLEAN;
|
||||
} else if ($fieldDefs->getType() === FieldType::INT) {
|
||||
$colSchema->type = Type::INTEGER;
|
||||
} else if ($fieldDefs->getType() === FieldType::FLOAT) {
|
||||
$colSchema->type = Type::NUMBER;
|
||||
} else if ($fieldDefs->getType() === FieldType::VARCHAR) {
|
||||
$colSchema->type = Type::STRING;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MAX_LENGTH)) {
|
||||
$colSchema->maxLength = $fieldDefs->getParam(FieldParam::MAX_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
return get_object_vars($colSchema);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class LinkParentType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$idSchema = (object) [];
|
||||
$idSchema->type = Type::STRING;
|
||||
$idSchema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
$idSchema->description = "A foreign record ID.";
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$idSchema->type = [
|
||||
$idSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$typeSchema = (object) [];
|
||||
$typeSchema->type = Type::STRING;
|
||||
$typeSchema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$typeSchema->type = [
|
||||
$typeSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
if ($fieldDefs->getParam('entityList')) {
|
||||
$typeSchema->enum = $fieldDefs->getParam('entityList');
|
||||
}
|
||||
|
||||
$typeSchema->description = "An entity type.";
|
||||
|
||||
$nameSchema = (object) [];
|
||||
$nameSchema->type = Type::STRING;
|
||||
$nameSchema->readOnly = true;
|
||||
|
||||
$nameSchema->type = [
|
||||
$nameSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field . 'Id';
|
||||
$required[] = $field . 'Type';
|
||||
}
|
||||
|
||||
$nameSchema->description = 'A foreign record name.';
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field . 'Id' => get_object_vars($idSchema),
|
||||
$field . 'Type' => get_object_vars($typeSchema),
|
||||
$field . 'Name' => get_object_vars($nameSchema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class LinkType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
$linkDefs = $this->defs->getEntity($entityType)->tryGetRelation($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if ($linkDefs && $linkDefs->tryGetForeignEntityType()) {
|
||||
$foreignEntityType = $linkDefs->tryGetForeignEntityType();
|
||||
|
||||
$schema->description = "An ID of the record of the $foreignEntityType type.";
|
||||
}
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$nameSchema = (object) [];
|
||||
$nameSchema->type = Type::STRING;
|
||||
$nameSchema->readOnly = true;
|
||||
$nameSchema->type = [
|
||||
$nameSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
|
||||
$nameSchema->description = 'A foreign record name.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field . 'Id';
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field . 'Id' => get_object_vars($schema),
|
||||
$field . 'Name' => get_object_vars($nameSchema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
use Espo\Tools\OpenApi\Util\EnumOptionsProvider;
|
||||
|
||||
class MultiEnumType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
private EnumOptionsProvider $optionsProvider,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::ARRAY;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
$optionList = $this->optionsProvider->get($fieldDefs);
|
||||
|
||||
$itemSchema = (object) [
|
||||
'type' => Type::STRING,
|
||||
];
|
||||
|
||||
if ($optionList) {
|
||||
$itemSchema->enum = $optionList;
|
||||
}
|
||||
|
||||
$schema->items = get_object_vars($itemSchema);
|
||||
|
||||
$schema->description = 'A multi-enum.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class NoSupport implements FieldSchemaBuilder
|
||||
{
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class NumberType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->readOnly = true;
|
||||
|
||||
$schema->description = 'A number. Auto-incrementing with a prefix.';
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class PhoneType implements FieldSchemaBuilder
|
||||
{
|
||||
const int DEFAULT_MAX_LENGTH = 36;
|
||||
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->maxLength = self::DEFAULT_MAX_LENGTH;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A primary phone number.';
|
||||
|
||||
$itemSchema = (object) [];
|
||||
$itemSchema->type = Type::OBJECT;
|
||||
$itemSchema->properties = [
|
||||
'phoneNumber' => [
|
||||
'type' => Type::STRING,
|
||||
],
|
||||
'primary' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'optOut' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'invalid' => [
|
||||
'type' => Type::BOOLEAN,
|
||||
],
|
||||
'type' => [
|
||||
'type' => Type::STRING,
|
||||
'enum' => $fieldDefs->getParam('typeList'),
|
||||
],
|
||||
];
|
||||
$itemSchema->required = [
|
||||
'phoneNumber',
|
||||
'primary',
|
||||
];
|
||||
|
||||
$dataSchema = (object) [];
|
||||
$dataSchema->type = Type::ARRAY;
|
||||
$dataSchema->items = get_object_vars($itemSchema);
|
||||
$dataSchema->type = [
|
||||
$dataSchema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
$dataSchema->description = 'Multiple phone numbers.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
$field . 'Data' => get_object_vars($dataSchema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class TextType implements FieldSchemaBuilder
|
||||
{
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::MAX_LENGTH)) {
|
||||
$schema->maxLength = $fieldDefs->getParam(FieldParam::MAX_LENGTH);
|
||||
}
|
||||
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A multi-line text.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\FieldSchemaBuilders;
|
||||
|
||||
use Espo\Tools\OpenApi\Type;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\Params\FieldParam;
|
||||
use Espo\Tools\OpenApi\FieldSchemaBuilder;
|
||||
use Espo\Tools\OpenApi\FieldSchemaResult;
|
||||
|
||||
class VarcharType implements FieldSchemaBuilder
|
||||
{
|
||||
const int DEFAULT_MAX_LENGTH = 255;
|
||||
|
||||
public function __construct(
|
||||
private Defs $defs,
|
||||
) {}
|
||||
|
||||
public function build(string $entityType, string $field): FieldSchemaResult
|
||||
{
|
||||
$fieldDefs = $this->defs->getEntity($entityType)->getField($field);
|
||||
|
||||
$schema = (object) [];
|
||||
$schema->type = Type::STRING;
|
||||
$schema->maxLength = $fieldDefs->getParam(FieldParam::MAX_LENGTH) ?? self::DEFAULT_MAX_LENGTH;
|
||||
$schema->readOnly = $fieldDefs->getParam(FieldParam::READ_ONLY) ?? false;
|
||||
|
||||
if (!$fieldDefs->getParam(FieldParam::REQUIRED)) {
|
||||
$schema->type = [
|
||||
$schema->type,
|
||||
Type::NULL,
|
||||
];
|
||||
}
|
||||
|
||||
$schema->description = 'A one-line string.';
|
||||
|
||||
$required = [];
|
||||
|
||||
if ($fieldDefs->getParam(FieldParam::REQUIRED) && !$fieldDefs->getParam(FieldParam::DEFAULT)) {
|
||||
$required[] = $field;
|
||||
}
|
||||
|
||||
return new FieldSchemaResult(
|
||||
properties: [
|
||||
$field => get_object_vars($schema),
|
||||
],
|
||||
required: $required,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi;
|
||||
|
||||
class FieldSchemaResult
|
||||
{
|
||||
/**
|
||||
* @param array<string, array<string, mixed>> $properties
|
||||
* @param string[] $required
|
||||
*/
|
||||
public function __construct(
|
||||
public array $properties,
|
||||
public array $required = [],
|
||||
) {}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi;
|
||||
|
||||
use Espo\Core\Binding\BindingContainerBuilder;
|
||||
use Espo\Core\InjectableFactory;
|
||||
|
||||
class ProviderFactory
|
||||
{
|
||||
public function __construct(
|
||||
private InjectableFactory $injectableFactory,
|
||||
) {}
|
||||
|
||||
public function create(): Provider
|
||||
{
|
||||
return $this->injectableFactory->createWithBinding(
|
||||
Provider::class,
|
||||
BindingContainerBuilder::create()
|
||||
->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi;
|
||||
|
||||
class Type
|
||||
{
|
||||
const string INTEGER = 'integer';
|
||||
const string NUMBER = 'number';
|
||||
const string STRING = 'string';
|
||||
const string BOOLEAN = 'boolean';
|
||||
const string OBJECT = 'object';
|
||||
const string ARRAY = 'array';
|
||||
const string NULL = 'null';
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Espo\Tools\OpenApi\Util;
|
||||
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\ORM\Defs\FieldDefs;
|
||||
|
||||
class EnumOptionsProvider
|
||||
{
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return ?string[]
|
||||
*/
|
||||
public function get(FieldDefs $fieldDefs): ?array
|
||||
{
|
||||
/** @var ?string $path */
|
||||
$path = $fieldDefs->getParam('optionsPath');
|
||||
/** @var ?string $path */
|
||||
$ref = $fieldDefs->getParam('optionsReference');
|
||||
|
||||
if (!$path && $ref && str_contains($ref, '.')) {
|
||||
[$refEntityType, $refField] = explode('.', $ref);
|
||||
|
||||
$path = "entityDefs.$refEntityType.fields.$refField.options";
|
||||
}
|
||||
|
||||
/** @var ?string[] $optionList */
|
||||
$optionList = $path ?
|
||||
$this->metadata->get($path) :
|
||||
$fieldDefs->getParam('options');
|
||||
|
||||
return $optionList;
|
||||
}
|
||||
}
|
||||
Generated
+2
-2
@@ -9909,9 +9909,9 @@
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"laminas/laminas-ldap": 20,
|
||||
"laminas/laminas-mail": 20,
|
||||
"laminas/laminas-mime": 20,
|
||||
"laminas/laminas-ldap": 20,
|
||||
"zordius/lightncandy": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
@@ -9930,6 +9930,6 @@
|
||||
"ext-pdo": "*",
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -1120,6 +1120,10 @@
|
||||
"type": "boolean",
|
||||
"description": "Disables the ability perform mass-update on the field. As of v8.4."
|
||||
},
|
||||
"apiSpecDisabled": {
|
||||
"type": "boolean",
|
||||
"description": "Disable field definitions in API specification. As of v9.3."
|
||||
},
|
||||
"isPersonalData": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the field may contain personal data."
|
||||
|
||||
Reference in New Issue
Block a user