From 051da082f16e4fa674b4faf9f75cbffa96b5b09c Mon Sep 17 00:00:00 2001 From: Laura Trotta Date: Thu, 12 Jun 2025 15:56:32 +0200 Subject: [PATCH 1/4] added dictionary rule eslint --- specification/eslint.config.js | 1 + validator/README.md | 1 + validator/eslint-plugin-es-spec.js | 2 + validator/rules/dictionary-key-is-string.js | 64 +++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 validator/rules/dictionary-key-is-string.js diff --git a/specification/eslint.config.js b/specification/eslint.config.js index 2244cadc45..398377dbad 100644 --- a/specification/eslint.config.js +++ b/specification/eslint.config.js @@ -32,6 +32,7 @@ export default defineConfig({ plugins: { 'es-spec-validator': validator }, rules: { 'es-spec-validator/single-key-dictionary-key-is-string': 'error', + 'es-spec-validator/dictionary-key-is-string': 'error', 'es-spec-validator/invalid-node-types': 'warn' } }) diff --git a/validator/README.md b/validator/README.md index c43b60e18b..d64b769259 100644 --- a/validator/README.md +++ b/validator/README.md @@ -8,6 +8,7 @@ It is configured [in the specification directory](../specification/eslint.config | Name | Description | | - | - | | `single-key-dictionary-key-is-string` | `SingleKeyDictionary` keys must be strings. | +| `dictionary-key-is-string` | `Dictionary` keys must be strings. | | `invalid-node-types` | The spec uses a subset of TypeScript, so some types, clauses and expressions are not allowed. | ## Usage diff --git a/validator/eslint-plugin-es-spec.js b/validator/eslint-plugin-es-spec.js index 95537a4d34..03693d5708 100644 --- a/validator/eslint-plugin-es-spec.js +++ b/validator/eslint-plugin-es-spec.js @@ -17,11 +17,13 @@ * under the License. */ import singleKeyDict from './rules/single-key-dictionary-key-is-string.js' +import Dict from './rules/dictionary-key-is-string.js' import invalidNodeTypes from './rules/invalid-node-types.js' export default { rules: { 'single-key-dictionary-key-is-string': singleKeyDict, + 'dictionary-key-is-string': Dict, 'invalid-node-types': invalidNodeTypes, } } diff --git a/validator/rules/dictionary-key-is-string.js b/validator/rules/dictionary-key-is-string.js new file mode 100644 index 0000000000..d5c8eb1897 --- /dev/null +++ b/validator/rules/dictionary-key-is-string.js @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { ESLintUtils } from '@typescript-eslint/utils'; +import ts from 'typescript' + +const createRule = ESLintUtils.RuleCreator(name => `https://example.com/rule/${name}`) + +export default createRule({ + name: 'dictionary-key-is-string', + create(context) { + return { + TSTypeReference(node) { + if (node.typeName.name === 'Dictionary') { + const key = node.typeArguments.params[0] + switch (key.type) { + case 'TSTypeReference': + // trace the reference to its original type definition + const services = ESLintUtils.getParserServices(context) + const type = services.getTypeAtLocation(key) + + // check that the type is a string or an enum (enum members evaluate to strings) + if (type.intrinsicName !== 'string' && !(type.symbol?.flags & ts.SymbolFlags.RegularEnum)) { + context.report({ node, messageId: 'stringKey' }) + } + break + case 'TSStringKeyword': + // type is string, skip + break + default: + // unknown type! + context.report({ node, messageId: 'stringKey' }) + break + } + } + }, + } + }, + meta: { + docs: { + description: 'Dictionary keys must be strings', + }, + messages: { + stringKey: "Dictionary's key must be a string" + }, + type: 'suggestion', + }, + defaultOptions: [] +}) From 76e033a5da4c1c7c748a1c96c0192a15dc16f4a0 Mon Sep 17 00:00:00 2001 From: Laura Trotta Date: Thu, 12 Jun 2025 16:36:32 +0200 Subject: [PATCH 2/4] fix taskid type --- output/openapi/elasticsearch-openapi.json | 9 +------- .../elasticsearch-serverless-openapi.json | 9 +------- output/schema/schema.json | 22 +++++-------------- output/typescript/types.ts | 2 +- specification/_types/common.ts | 2 +- 5 files changed, 9 insertions(+), 35 deletions(-) diff --git a/output/openapi/elasticsearch-openapi.json b/output/openapi/elasticsearch-openapi.json index 8580d082a6..ff3c70ca18 100644 --- a/output/openapi/elasticsearch-openapi.json +++ b/output/openapi/elasticsearch-openapi.json @@ -79941,14 +79941,7 @@ ] }, "_types.TaskId": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "tasks._types.TaskListResponseBase": { "type": "object", diff --git a/output/openapi/elasticsearch-serverless-openapi.json b/output/openapi/elasticsearch-serverless-openapi.json index 024c0d0094..06e4482539 100644 --- a/output/openapi/elasticsearch-serverless-openapi.json +++ b/output/openapi/elasticsearch-serverless-openapi.json @@ -51867,14 +51867,7 @@ ] }, "_types.TaskId": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "number" - } - ] + "type": "string" }, "enrich.execute_policy.ExecuteEnrichPolicyStatus": { "type": "object", diff --git a/output/schema/schema.json b/output/schema/schema.json index 330bbd13eb..ff889ef351 100644 --- a/output/schema/schema.json +++ b/output/schema/schema.json @@ -54792,23 +54792,11 @@ }, "specLocation": "_types/common.ts#L129-L129", "type": { - "kind": "union_of", - "items": [ - { - "kind": "instance_of", - "type": { - "name": "string", - "namespace": "_builtins" - } - }, - { - "kind": "instance_of", - "type": { - "name": "integer", - "namespace": "_types" - } - } - ] + "kind": "instance_of", + "type": { + "name": "string", + "namespace": "_builtins" + } } }, { diff --git a/output/typescript/types.ts b/output/typescript/types.ts index 0c5884d960..71a0bb5359 100644 --- a/output/typescript/types.ts +++ b/output/typescript/types.ts @@ -2962,7 +2962,7 @@ export interface TaskFailure { reason: ErrorCause } -export type TaskId = string | integer +export type TaskId = string export interface TextEmbedding { model_id: string diff --git a/specification/_types/common.ts b/specification/_types/common.ts index 73847e459e..00343529a8 100644 --- a/specification/_types/common.ts +++ b/specification/_types/common.ts @@ -126,7 +126,7 @@ export type SequenceNumber = long export type PropertyName = string export type RelationName = string -export type TaskId = string | integer +export type TaskId = string /** @doc_id fuzziness */ export type Fuzziness = string | integer /** @doc_id query-dsl-multi-term-rewrite */ From 58ec95dd7d9b7f400ebdff768faeb74b07db7f0c Mon Sep 17 00:00:00 2001 From: Laura Trotta Date: Thu, 12 Jun 2025 18:40:32 +0200 Subject: [PATCH 3/4] add test --- .../test/dictionary-key-is-string.test.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 validator/test/dictionary-key-is-string.test.js diff --git a/validator/test/dictionary-key-is-string.test.js b/validator/test/dictionary-key-is-string.test.js new file mode 100644 index 0000000000..bf8d81b9a6 --- /dev/null +++ b/validator/test/dictionary-key-is-string.test.js @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { RuleTester } from '@typescript-eslint/rule-tester' +import rule from '../rules/dictionary-key-is-string.js' + +const ruleTester = new RuleTester({ + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProject: ['*.ts*'], + }, + tsconfigRootDir: import.meta.dirname, + }, + }, +}) + +ruleTester.run('dictionary-key-is-string', rule, { + valid: [ + `type MyDict = Dictionary`, + `type MyDict = Dictionary`, + `type MyDict = Dictionary`, + `enum MyEnum { foo, bar, baz } + type MyDict = Dictionary`, + ], + invalid: [ + { + code: + `type MyKey = string | boolean + type MyDict = Dictionary`, + errors: [{ messageId: 'stringKey' }] + }, + { + code: `type MyDict = Dictionary`, + errors: [{ messageId: 'stringKey' }] + }, + { + code: `type MyDict = Dictionary`, + errors: [{ messageId: 'stringKey' }] + }, + { + code: `type MyDict = Dictionary`, + errors: [{ messageId: 'stringKey' }] + } + ], +}) From 809187aa8584cc1c6027e785019a0cd7f9cb1d6a Mon Sep 17 00:00:00 2001 From: Laura Trotta Date: Fri, 13 Jun 2025 10:32:53 +0200 Subject: [PATCH 4/4] address nit --- validator/eslint-plugin-es-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/eslint-plugin-es-spec.js b/validator/eslint-plugin-es-spec.js index 03693d5708..e74b363579 100644 --- a/validator/eslint-plugin-es-spec.js +++ b/validator/eslint-plugin-es-spec.js @@ -17,13 +17,13 @@ * under the License. */ import singleKeyDict from './rules/single-key-dictionary-key-is-string.js' -import Dict from './rules/dictionary-key-is-string.js' +import dict from './rules/dictionary-key-is-string.js' import invalidNodeTypes from './rules/invalid-node-types.js' export default { rules: { 'single-key-dictionary-key-is-string': singleKeyDict, - 'dictionary-key-is-string': Dict, + 'dictionary-key-is-string': dict, 'invalid-node-types': invalidNodeTypes, } }