From 3ae14bd7972b9093275280c75f5b0e532046be5d Mon Sep 17 00:00:00 2001 From: saimedhi Date: Tue, 28 May 2024 15:31:31 -0700 Subject: [PATCH] Enhanced Generator Code to generate Endpoints Using OpenSearch API Specifications Signed-off-by: saimedhi --- composer.json | 3 +- src/OpenSearch/Endpoints/AbstractEndpoint.php | 1 + .../Namespaces/IndicesNamespace.php | 2 +- src/OpenSearch/Namespaces/SqlNamespace.php | 1 + src/OpenSearch/Transport.php | 2 +- .../StaticConnectionPoolTest.php | 2 +- tests/Connections/ConnectionTest.php | 1 + util/ActionTest.php | 6 +- util/ClientEndpoint.php | 8 +- util/Endpoint.php | 64 ++-- util/GenerateDocExamples.php | 2 +- util/GenerateEndpoints.php | 320 ++++++++++++++---- util/NamespaceEndpoint.php | 33 +- util/license_header.php | 62 ++++ util/template/client-class | 26 +- util/template/deprecated-param-master-timeout | 4 + util/template/endpoint-bulk-class | 2 +- util/template/endpoint-class | 10 +- util/template/namespace-class | 5 +- util/template/test/unit-test-oss | 2 +- util/template/test/unit-test-skipped | 2 +- 21 files changed, 434 insertions(+), 124 deletions(-) create mode 100644 util/license_header.php create mode 100644 util/template/deprecated-param-master-timeout diff --git a/composer.json b/composer.json index c73a81bd0..83ed13266 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "ext-json": ">=1.3.7", "ext-curl": "*", "ezimuel/ringphp": "^1.1.2", - "psr/log": "^1|^2|^3" + "psr/log": "^1|^2|^3", + "symfony/yaml": "*" }, "require-dev": { "ext-zip": "*", diff --git a/src/OpenSearch/Endpoints/AbstractEndpoint.php b/src/OpenSearch/Endpoints/AbstractEndpoint.php index ca8a7a038..62bc138e9 100644 --- a/src/OpenSearch/Endpoints/AbstractEndpoint.php +++ b/src/OpenSearch/Endpoints/AbstractEndpoint.php @@ -23,6 +23,7 @@ use OpenSearch\Common\Exceptions\UnexpectedValueException; use OpenSearch\Serializers\SerializerInterface; + use function array_filter; abstract class AbstractEndpoint diff --git a/src/OpenSearch/Namespaces/IndicesNamespace.php b/src/OpenSearch/Namespaces/IndicesNamespace.php index 488b65924..fc95770d4 100644 --- a/src/OpenSearch/Namespaces/IndicesNamespace.php +++ b/src/OpenSearch/Namespaces/IndicesNamespace.php @@ -1175,7 +1175,7 @@ public function getDataStream(array $params = []) } /** * $params['index'] = (list) A comma-separated list of index names to refresh analyzers for - * + * * @param array $params Associative array of parameters * @return array */ diff --git a/src/OpenSearch/Namespaces/SqlNamespace.php b/src/OpenSearch/Namespaces/SqlNamespace.php index 8f747d2b1..19285cfb0 100644 --- a/src/OpenSearch/Namespaces/SqlNamespace.php +++ b/src/OpenSearch/Namespaces/SqlNamespace.php @@ -14,6 +14,7 @@ namespace OpenSearch\Namespaces; use OpenSearch\Endpoints\AbstractEndpoint; + use function array_filter; class SqlNamespace extends AbstractNamespace diff --git a/src/OpenSearch/Transport.php b/src/OpenSearch/Transport.php index 09a9a7d3f..d7d7fb255 100644 --- a/src/OpenSearch/Transport.php +++ b/src/OpenSearch/Transport.php @@ -122,7 +122,7 @@ public function performRequest(string $method, string $uri, array $params = [], //onSuccess function ($response) { $this->retryAttempts = 0; - // Note, this could be a 4xx or 5xx error + // Note, this could be a 4xx or 5xx error }, //onFailure function ($response) { diff --git a/tests/ConnectionPool/StaticConnectionPoolTest.php b/tests/ConnectionPool/StaticConnectionPoolTest.php index 15958f6ac..5a4e0b85c 100644 --- a/tests/ConnectionPool/StaticConnectionPoolTest.php +++ b/tests/ConnectionPool/StaticConnectionPoolTest.php @@ -190,7 +190,7 @@ public function testAllExceptLastHostFailPingRevivesPreSkip() $goodConnection->expects('isAlive')->andReturns(false); $goodConnection->expects('markDead'); $goodConnection->expects('getPingFailures')->andReturns(0); - $goodConnection->expects('getLastPing')->andReturns(time()-10000); + $goodConnection->expects('getLastPing')->andReturns(time() - 10000); $connections[] = $goodConnection; diff --git a/tests/Connections/ConnectionTest.php b/tests/Connections/ConnectionTest.php index cd4f76ad5..b964b341a 100644 --- a/tests/Connections/ConnectionTest.php +++ b/tests/Connections/ConnectionTest.php @@ -31,6 +31,7 @@ use Exception; use Psr\Log\LoggerInterface; use ReflectionClass; + use function base64_encode; use function random_bytes; diff --git a/util/ActionTest.php b/util/ActionTest.php index 9a24f272c..fad6cd2d3 100644 --- a/util/ActionTest.php +++ b/util/ActionTest.php @@ -244,7 +244,7 @@ private function match(array $actions) if (is_string($actions[$key]) && substr($actions[$key], 0, 1) !== '$') { $expected = sprintf("'%s'", addslashes($actions[$key])); } elseif (is_string($actions[$key]) && substr($actions[$key], 0, 2) === '${') { - $expected = sprintf("\$%s", substr($actions[$key], 2, strlen($actions[$key])-3)); + $expected = sprintf("\$%s", substr($actions[$key], 2, strlen($actions[$key]) - 3)); } elseif (is_bool($actions[$key])) { $expected = $actions[$key] ? 'true' : 'false'; } elseif (is_array($actions[$key])) { @@ -422,8 +422,8 @@ private function convertDotToArrow(string $dot) $result = str_replace('.', '()->', $dot); $tot = strlen($result); for ($i = 0; $i < $tot; $i++) { - if ($result[$i] === '_' && ($i+1) < $tot) { - $result[$i+1] = strtoupper($result[$i+1]); + if ($result[$i] === '_' && ($i + 1) < $tot) { + $result[$i + 1] = strtoupper($result[$i + 1]); } } return str_replace('_', '', $result); diff --git a/util/ClientEndpoint.php b/util/ClientEndpoint.php index c75d2761e..e0c3dc72b 100644 --- a/util/ClientEndpoint.php +++ b/util/ClientEndpoint.php @@ -34,14 +34,10 @@ class ClientEndpoint extends NamespaceEndpoint protected $endpoints = []; protected $endpointNames = []; protected $namespace = []; - protected $version; - protected $buildhash; - public function __construct(array $namespace, string $version, string $buildhash) + public function __construct(array $namespace) { $this->namespace = $namespace; - $this->version = $version; - $this->buildhash = $buildhash; } public function renderClass(): string @@ -108,8 +104,6 @@ public function renderClass(): string $functions .= $func; } $class = str_replace(':functions', $functions, $class); - $class = str_replace(':version', $this->version, $class); - $class = str_replace(':buildhash', $this->buildhash, $class); return $class; } diff --git a/util/Endpoint.php b/util/Endpoint.php index 637fe357f..37cb1a5cd 100644 --- a/util/Endpoint.php +++ b/util/Endpoint.php @@ -35,6 +35,7 @@ class Endpoint public const CHECK_OPTIONS_TEMPLATE = __DIR__ . '/template/check-options'; public const SET_BULK_BODY_TEMPLATE = __DIR__ . '/template/set-bulk-body'; public const DEPRECATED_PART = __DIR__ . '/template/deprecated'; + public const DEPRECATED_PARAM = __DIR__ . '/template/deprecated-param-master-timeout'; public const PHP_RESERVED_WORDS = [ 'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch', 'class', 'clone', 'const', 'continue', 'declare', 'default', 'die', 'do', @@ -58,8 +59,6 @@ class Endpoint public $name; public $apiName; protected $content; - protected $version; - protected $buildhash; protected $parts = []; protected $requiredParts = []; protected $useNamespace = []; @@ -69,8 +68,6 @@ class Endpoint public function __construct( string $fileName, string $content, - string $version, - string $buildhash ) { $this->apiName = basename($fileName, '.json'); $parts = explode('.', $this->apiName); @@ -95,8 +92,6 @@ public function __construct( )); } $this->content = $this->content[$this->apiName]; - $this->version = $version; - $this->buildhash = $buildhash; $this->parts = $this->getPartsFromContent($this->content); $this->requiredParts = $this->getRequiredParts($this->content); @@ -127,6 +122,8 @@ private function getRequiredParts(array $content): array } if (count($required) > 1) { return call_user_func_array('array_intersect', $required); + } elseif (count($required) === 1) { + return $required[0]; } return $required; } @@ -146,7 +143,7 @@ public function renderClass(): string } $class = str_replace( ':uri', - $this->extractUrl($this->content['url']['paths']), + trim($this->extractUrl($this->content['url']['paths'])), $class ); $class = str_replace( @@ -164,11 +161,15 @@ public function renderClass(): string // Set the HTTP method $action = $this->getMethod(); - if (!empty($this->content['body']) && - ($action === ['GET', 'POST'] || $action === ['POST', 'GET'])) { - $method = 'isset($this->body) ? \'POST\' : \'GET\''; + if ($action === ['POST', 'PUT'] && $this->getClassName() !== 'Bulk') { + $method = "'PUT'"; } else { - $method = sprintf("'%s'", reset($action)); + if (!empty($this->content['body']) && + ($action === ['GET', 'POST'] || $action === ['POST', 'GET'])) { + $method = 'isset($this->body) ? \'POST\' : \'GET\''; + } else { + $method = sprintf("'%s'", reset($action)); + } } $class = str_replace(':method', $method, $class); @@ -186,7 +187,7 @@ public function renderClass(): string if (in_array($part, ['type', 'index', 'id'])) { continue; } - if (isset($value['type']) && $value['type'] === 'list') { + if (isset($value['type']) && $value['type'] === 'array') { $parts .= $this->getSetPartList($part); } else { $parts .= $this->getSetPart($part); @@ -194,11 +195,36 @@ public function renderClass(): string } $class = str_replace(':set-parts', $parts, $class); $class = str_replace(':endpoint', $this->getClassName(), $class); - $class = str_replace(':version', $this->version, $class); - $class = str_replace(':buildhash', $this->buildhash, $class); $class = str_replace(':use-namespace', $this->getNamespaces(), $class); $class = str_replace(':properties', $this->getProperties(), $class); + if (isset($this->content['params']['master_timeout'])) { + $deprecatedContent = file_get_contents(self::DEPRECATED_PARAM); + $class = str_replace(':master_timeout_ParamDeprecation', $deprecatedContent, $class); + } else { + $class = preg_replace('/\s*:master_timeout_ParamDeprecation\s*\}/', '}', $class); + } + + // Adding licence header + $currentDir = dirname(__FILE__); + $baseDir = dirname($currentDir); + $EndpointName = $this->getClassName(); + + if (!empty($this->namespace)) { + $filePath = $baseDir . "/src/OpenSearch/Endpoints/$this->namespace/$EndpointName.php"; + } else { + $filePath = $baseDir . "/src/OpenSearch/Endpoints/$EndpointName.php"; + } + + if (file_exists($filePath)) { + $content = file_get_contents($filePath); + if (strpos($content, 'Copyright OpenSearch') !== false) { + $pattern = '/\/\*\*.*?\*\//s'; + if (preg_match($pattern, $content, $matches)) { + $class = str_replace('declare(strict_types=1);', 'declare(strict_types=1);' . PHP_EOL . PHP_EOL . $matches[0], $class); + } + } + } return str_replace(':apiname', $this->apiName, $class); } @@ -291,7 +317,7 @@ private function extractUrl(array $paths): string $check = sprintf("isset(\$%s)", $parts[0]); } $url = str_replace('{' . $parts[0] .'}', '$' . $parts[0], $path); - for ($i=1; $irequiredParts)) { continue; @@ -351,7 +377,7 @@ private function extractPaths(array $paths): array $urls = $this->removePathWithSameParts($paths); // Order the url based on descendant length usort($urls, function ($a, $b) { - return strlen($b)-strlen($a); + return strlen($b) - strlen($a); }); return $urls; @@ -494,8 +520,8 @@ private function extractPartsDescription(int $space): string " * \$params['%s']%s = %s(%s) %s%s\n", $part, str_repeat(' ', $space - strlen($part)), - $part ==='type' || (isset($values['deprecated']) && $values['deprecated']) ? 'DEPRECATED ' : '', - $values['type'], + $part === 'type' || (isset($values['deprecated']) && $values['deprecated']) ? 'DEPRECATED ' : '', + $values['type'] ?? 'any', $values['description'] ?? '', in_array($part, $this->requiredParts) ? ' (Required)' : '' ); @@ -518,7 +544,7 @@ private function extractParamsDescription(int $space): string " * \$params['%s']%s = (%s) %s%s%s%s\n", $param, str_repeat(' ', $space - strlen($param)), - $values['type'], + $values['type'] ?? 'any', $values['description'] ?? '', isset($values['required']) && $values['required'] ? ' (Required)' : '', isset($values['options']) ? sprintf(" (Options = %s)", implode(',', $values['options'])) : '', diff --git a/util/GenerateDocExamples.php b/util/GenerateDocExamples.php index af25c4700..3d7b35abc 100644 --- a/util/GenerateDocExamples.php +++ b/util/GenerateDocExamples.php @@ -97,7 +97,7 @@ printf("\nLanguage statistics:\n"); foreach ($langs as $lang => $num) { - printf("%-8s: %4d (%.2f%% completed)\n", $lang, $num, (100/$tot)*$num); + printf("%-8s: %4d (%.2f%% completed)\n", $lang, $num, (100 / $tot) * $num); } printf("\n"); diff --git a/util/GenerateEndpoints.php b/util/GenerateEndpoints.php index 1d8d6ded6..cf0e4d534 100644 --- a/util/GenerateEndpoints.php +++ b/util/GenerateEndpoints.php @@ -26,51 +26,31 @@ use OpenSearch\Util\Endpoint; use OpenSearch\Util\NamespaceEndpoint; use OpenSearch\Tests\Utility; +use Symfony\Component\Yaml\Yaml; require_once dirname(__DIR__) . '/vendor/autoload.php'; - -try { - $client = Utility::getClient(); -} catch (RuntimeException $e) { - printf("ERROR: I cannot find STACK_VERSION and TEST_SUITE environment variables\n"); - exit(1); -} - -try { - $serverInfo = $client->info(); -} catch (NoNodesAvailableException $e) { - printf("ERROR: Host %s is offline\n", Utility::getHost()); - exit(1); -} -$version = $serverInfo['version']['number']; -$buildHash = $serverInfo['version']['build_hash']; - -if (version_compare($version, '7.4.0', '<')) { - printf("Error: the ES version must be >= 7.4.0\n"); - exit(1); -} - -$backupFileName = sprintf( - "%s/backup_endpoint_namespace_%s.zip", - __DIR__, - Client::VERSION -); - -printf("Backup Endpoints and Namespaces in:\n%s\n", $backupFileName); -backup($backupFileName); +require_once dirname(__DIR__) . '/util/license_header.php'; $start = microtime(true); printf("Generating endpoints for OpenSearch\n"); $success = true; -// Check if the rest-spec folder with the build hash exists -if (!is_dir(sprintf("%s/rest-spec/%s", __DIR__, $buildHash))) { - printf("ERROR: I cannot find the rest-spec for build hash %s\n", $buildHash); - printf("You need to execute 'php util/RestSpecRunner.php'\n"); - exit(1); + +// Load the OpenAPI specification file +$url = "https://github.com/opensearch-project/opensearch-api-specification/releases/download/main/opensearch-openapi.yaml"; +$yamlContent = file_get_contents($url); +$data = Yaml::parse($yamlContent); + +$list_of_dicts = []; +foreach ($data["paths"] as $path => $pathDetails) { + + foreach ($pathDetails as $method => $methodDetails) { + $methodDetails["path"] = $path; + $methodDetails["method"] = $method; + $list_of_dicts[] = $methodDetails; + } } -$files = glob(sprintf("%s/rest-spec/%s/rest-api-spec/api/*.json", __DIR__, $buildHash)); $outputDir = __DIR__ . "/output"; if (!file_exists($outputDir)) { @@ -85,37 +65,253 @@ $countEndpoint = 0; $namespaces = []; -// Generate endpoints -foreach ($files as $file) { - if (stripos($file, 'xpack') !== false) { - continue; - } +foreach ($list_of_dicts as $index => $endpoint) { - if (empty($file) || (basename($file) === '_common.json')) { - continue; + if (array_key_exists("parameters", $endpoint)) { + + $params = []; + $parts = []; + // Iterate over the list of parameters and update them + foreach ($endpoint["parameters"] as $param_ref) { + $param_ref_value = substr($param_ref["$"."ref"], strrpos($param_ref["$"."ref"], '/') + 1); + $param = $data["components"]["parameters"][$param_ref_value]; + if (isset($param["schema"]) && isset($param["schema"]["$"."ref"])) { + $schema_path_ref = substr($param["schema"]["$"."ref"], strrpos($param["schema"]["$"."ref"], '/') + 1); + $param["schema"] = $data["components"]["schemas"][$schema_path_ref]; + $params[] = $param; + } else { + $params[] = $param; + } + } + var_dump($params); + + // Iterate over the list of updated parameters to separate "parts" from "params" + $params_copy = $params; + + foreach ($params_copy as $key => $param) { + if ($param["in"] === "path") { + $parts[] = $param; + unset($params[$key]); + } + } + + // Convert "params" and "parts" into the structure required for generator. + $params_new = []; + $parts_new = []; + + foreach ($params as $param) { + $param_dict = []; + + if (isset($param['description'])) { + $param_dict['description'] = str_replace("\n", "", $param['description']); + } + + if (isset($param['schema']['type'])) { + $param_dict['type'] = $param['schema']['type']; + } + + if (isset($param['schema']['default'])) { + $param_dict['default'] = $param['schema']['default']; + } + + if (isset($param['schema']['enum'])) { + $param_dict['type'] = 'enum'; + $param_dict['options'] = $param['schema']['enum']; + } + + if (isset($param['deprecated'])) { + $param_dict['deprecated'] = $param['deprecated']; + } + + if (isset($param['x-deprecation-message'])) { + $param_dict['deprecation_message'] = $param['x-deprecation-message']; + } + + $params_new[$param['name']] = $param_dict; + } + + if ($endpoint['x-operation-group'] !== 'nodes.hot_threads' && isset($params_new['type'])) { + unset($params_new['type']); + } + if (!empty($params_new)) { + $endpoint['params'] = $params_new; + } + + foreach ($parts as $part) { + $parts_dict = []; + + if (isset($part['schema']['type'])) { + $parts_dict['type'] = $part['schema']['type']; + } elseif (isset($part['schema']['oneOf'])) { + foreach ($part['schema']['oneOf'] as $item) { + if (isset($item['type'])) { + $parts_dict['type'] = $item['type']; + break; + } + } + } + + if (isset($part['description'])) { + $parts_dict['description'] = str_replace("\n", " ", $part['description']); + } + + if (isset($part['schema']['x-enum-options'])) { + $parts_dict['options'] = $part['schema']['x-enum-options']; + } + + if (isset($part['deprecated'])) { + $parts_dict['deprecated'] = $part['deprecated']; + } + + $parts_new[$part['name']] = $parts_dict; + } + if (!empty($parts_new)) { + $endpoint['parts'] = $parts_new; + } + $list_of_dicts[$index] = $endpoint; } - printf("Generating %s...", basename($file)); +} +$files = []; +// Sort $list_of_dicts by the value of the "x-operation-group" key +usort($list_of_dicts, function ($a, $b) { + return $a['x-operation-group'] <=> $b['x-operation-group']; +}); + +// Group $list_of_dicts by the value of the "x-operation-group" key +$grouped = []; +foreach ($list_of_dicts as $dict) { + $grouped[$dict['x-operation-group']][] = $dict; +} - $endpoint = new Endpoint($file, file_get_contents($file), $version, $buildHash); +foreach ($grouped as $key => $value) { + $api = []; - $dir = $endpointDir . NamespaceEndpoint::normalizeName($endpoint->namespace); - if (!file_exists($dir)) { - mkdir($dir); + // Extract the namespace and name from the 'x-operation-group' + if (strpos($key, '.') !== false) { + list($namespace, $name) = explode('.', $key); + } else { + $namespace = "__init__"; + $name = $key; } - $outputFile = sprintf("%s/%s.php", $dir, $endpoint->getClassName()); - file_put_contents( - $outputFile, - $endpoint->renderClass() - ); - if (!isValidPhpSyntax($outputFile)) { - printf("Error: syntax error in %s\n", $outputFile); - exit(1); + + // Group the data in the current group by the "path" key + $grouped_by_path = []; + foreach ($value as $dict) { + $grouped_by_path[$dict['path']][] = $dict; } - printf("done\n"); + $paths = []; + $all_paths_have_deprecation = true; + + foreach ($grouped_by_path as $path => $path_dicts) { + + $methods = []; + $parts_final = []; + $deprecated_path_dict = []; + + foreach ($path_dicts as $method_dict) { + $methods[] = strtoupper($method_dict['method']); + + if (!isset($api['documentation'])) { + $api['documentation'] = ['description' => $method_dict['description']]; + } + + if (isset($method_dict["x-version-deprecated"])) { + $deprecated_path_dict = array_merge($deprecated_path_dict, ["version" => $method_dict["x-version-deprecated"]]); + } + + if (isset($method_dict["x-deprecation-message"])) { + $deprecated_path_dict = array_merge($deprecated_path_dict, ["description" => $method_dict["x-deprecation-message"]]); + } else { + $all_paths_have_deprecation = false; + } + + if (!isset($api['params']) && isset($method_dict['params'])) { + $api['params'] = $method_dict['params']; + } + + if (!isset($api['body']) && isset($method_dict['requestBody']) && isset($method_dict['requestBody']['$ref'])) { + $requestbody_ref = explode('/', $method_dict['requestBody']['$ref']); + $requestbody_ref = end($requestbody_ref); + $body = ['required' => false]; + + if (isset($data['components']['requestBodies'][$requestbody_ref]['required'])) { + $body['required'] = $data['components']['requestBodies'][$requestbody_ref]['required']; + } + + if (isset($data['components']['requestBodies'][$requestbody_ref]['content']['application/x-ndjson'])) { + $requestbody_schema = $data['components']['requestBodies'][$requestbody_ref]['content']['application/x-ndjson']['schema']; + $body['serialize'] = "bulk"; + } else { + $requestbody_schema = $data['components']['requestBodies'][$requestbody_ref]['content']['application/json']['schema']; + } + + if (isset($requestbody_schema['description'])) { + $body['description'] = $requestbody_schema['description']; + } - $namespaces[$endpoint->namespace][] = $endpoint; - $countEndpoint++; + $api['body'] = $body; + } + + if (isset($method_dict['parts'])) { + $parts_final = array_merge($parts_final, $method_dict['parts']); + } + } + + // Update api dictionary with stability, visibility and headers + if (in_array('POST', $methods) || in_array('PUT', $methods)) { + $api['stability'] = 'stable'; + $api['visibility'] = 'public'; + $api['headers'] = [ + 'accept' => ['application/json'], + 'content_type' => ['application/json'], + ]; + } else { + $api['stability'] = 'stable'; + $api['visibility'] = 'public'; + $api['headers'] = ['accept' => ['application/json']]; + } + + $path_data = ['path' => $path, 'methods' => $methods]; + + if (!empty($deprecated_path_dict)) { + $path_data['deprecated'] = $deprecated_path_dict; + } + + if (!empty($parts_final)) { + $path_data['parts'] = $parts_final; + } + + $paths[] = $path_data; + } + + $api['url'] = ['paths' => $paths]; + $files[] = [$key => $api]; +} +// Generate endpoints +foreach ($files as $entry) { + foreach ($entry as $key => $api) { + + printf("Generating %s...", $key); + $entry_json = json_encode($entry); + $endpoint = new Endpoint($key . '.json', $entry_json); + + $dir = $endpointDir . NamespaceEndpoint::normalizeName($endpoint->namespace); + if (!file_exists($dir)) { + mkdir($dir); + } + $outputFile = sprintf("%s/%s.php", $dir, $endpoint->getClassName()); + file_put_contents($outputFile, $endpoint->renderClass()); + if (!isValidPhpSyntax($outputFile)) { + printf("Error: syntax error in %s\n", $outputFile); + exit(1); + } + + printf("done\n"); + + $namespaces[$endpoint->namespace][] = $endpoint; + $countEndpoint++; + } } // Generate namespaces @@ -129,7 +325,7 @@ foreach ($namespaces as $name => $endpoints) { if (empty($name)) { - $clientEndpoint = new ClientEndpoint(array_keys($namespaces), $version, $buildHash); + $clientEndpoint = new ClientEndpoint(array_keys($namespaces)); foreach ($endpoints as $ep) { $clientEndpoint->addEndpoint($ep); } @@ -144,7 +340,7 @@ $countNamespace++; continue; } - $namespace = new NamespaceEndpoint($name, $version, $buildHash); + $namespace = new NamespaceEndpoint($name); foreach ($endpoints as $ep) { $namespace->addEndpoint($ep); } @@ -164,6 +360,8 @@ printf("Copying the generated files to %s\n", $destDir); cleanFolders(); +fix_license_header($outputDir . "/Namespaces"); +fix_license_header($outputDir . "/Endpoints"); moveSubFolder($outputDir . "/Endpoints", $destDir . "/Endpoints"); moveSubFolder($outputDir . "/Namespaces", $destDir . "/Namespaces"); rename($outputDir . "/Client.php", $destDir . "/Client.php"); diff --git a/util/NamespaceEndpoint.php b/util/NamespaceEndpoint.php index 36ca224fd..98757a523 100644 --- a/util/NamespaceEndpoint.php +++ b/util/NamespaceEndpoint.php @@ -35,14 +35,10 @@ class NamespaceEndpoint protected $name; protected $endpoints = []; protected $endpointNames = []; - protected $version; - protected $buildhash; - public function __construct(string $name, string $version, string $buildhash) + public function __construct(string $name) { $this->name = $name; - $this->version = $version; - $this->buildhash = $buildhash; } public function renderClass(): string @@ -51,7 +47,24 @@ public function renderClass(): string throw new Exception("No endpoints has been added. I cannot render the class"); } $class = file_get_contents(static::NAMESPACE_CLASS_TEMPLATE); - $class = str_replace(':namespace', $this->getNamespaceName() . 'Namespace', $class); + $namespaceName = $this->getNamespaceName(). 'Namespace'; + $class = str_replace(':namespace', $namespaceName, $class); + + # Add license header + $currentDir = dirname(__FILE__); + $baseDir = dirname($currentDir); + $filePath = $baseDir . "/src/OpenSearch/Namespaces/$namespaceName.php"; + + if (file_exists($filePath)) { + $content = file_get_contents($filePath); + if (strpos($content, 'Copyright OpenSearch') !== false) { + $pattern = '/\/\*\*.*?\*\//s'; + if (preg_match($pattern, $content, $matches)) { + $class = str_replace('declare(strict_types=1);', 'declare(strict_types=1);' . PHP_EOL . PHP_EOL . $matches[0], $class); + + } + } + } $endpoints = ''; foreach ($this->endpoints as $endpoint) { @@ -67,8 +80,6 @@ public function renderClass(): string break; } $class = str_replace(':endpoints', $endpoints, $class); - $class = str_replace(':version', $this->version, $class); - $class = str_replace(':buildhash', $this->buildhash, $class); return $class; } @@ -113,7 +124,11 @@ protected function renderEndpoint(Endpoint $endpoint): string $param = str_replace(':param', 'body', file_get_contents(self::SET_PARAM_TEMPLATE)); $setParams .= str_replace(':Param', 'Body', $param); } - $code = str_replace(':extract', $extract, $code); + if (!empty($extract)) { + $code = str_replace(':extract', $extract, $code); + } else { + $code = str_replace("\n" . ':extract', '', $code); + } $code = str_replace(':setparam', $setParams, $code); if (empty($endpoint->namespace)) { diff --git a/util/license_header.php b/util/license_header.php new file mode 100644 index 000000000..fa1d0aa3d --- /dev/null +++ b/util/license_header.php @@ -0,0 +1,62 @@ + $line) { + if (strpos($line, 'declare(strict_types=1);') !== false) { + array_splice($lines, $i + 1, 0, "\n" . $LICENSE_HEADER . "\n"); + break; + } + } + file_put_contents($filepath, implode('', $lines)); + echo "Fixed " . realpath($filepath) . "\n"; +} + +function fix_license_header(string $path): void +{ + if (is_file($path)) { + if (doesFileNeedFix($path)) { + addHeaderToFile($path); + } + } elseif (is_dir($path)) { + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::LEAVES_ONLY + ); + foreach ($iterator as $file) { + if ($file->isFile() && doesFileNeedFix($file->getPathname())) { + addHeaderToFile($file->getPathname()); + } + } + } +} diff --git a/util/template/client-class b/util/template/client-class index 017b05e79..d167d8326 100644 --- a/util/template/client-class +++ b/util/template/client-class @@ -1,15 +1,28 @@ 'cluster_manager_timeout']; + } diff --git a/util/template/endpoint-bulk-class b/util/template/endpoint-bulk-class index d23ab612e..af887e1e4 100644 --- a/util/template/endpoint-bulk-class +++ b/util/template/endpoint-bulk-class @@ -1,6 +1,6 @@