Skip to content

Commit

Permalink
Merge branch 'main' into de_8_14/new_terms_suppression
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaliidm authored Apr 5, 2024
2 parents 5c36012 + e5adbbd commit d592c79
Show file tree
Hide file tree
Showing 57 changed files with 1,729 additions and 180 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,7 @@ module.exports = {
rules: {
'@kbn/i18n/strings_should_be_translated_with_i18n': 'warn',
'@kbn/i18n/i18n_translate_should_start_with_the_right_id': 'warn',
'@kbn/i18n/formatted_message_should_start_with_the_right_id': 'warn',
},
},
{
Expand Down
3 changes: 3 additions & 0 deletions docs/management/connectors/action-types/jira.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Title:: A title for the issue, used for searching the contents of the knowledge
Description:: The details about the incident.
Parent:: The ID or key of the parent issue. Only for `Subtask` issue types.
Additional comments:: Additional information for the client, such as how to troubleshoot the issue.
Additional fields::
An object that contains custom field identifiers and their values. These custom fields must comply with your Jira policies; they are not validated by the connector. For example, if a rule action does not include custom fields that are mandatory, the action might fail.


[float]
[[jira-connector-networking-configuration]]
Expand Down
Binary file modified docs/management/connectors/images/jira-params-test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { findKey } from 'lodash';

export function getI18nIdentifierFromFilePath(fileName: string, cwd: string) {
const { dir } = parse(fileName);

const relativePathToFile = dir.replace(cwd, '');

// We need to match the path of the file that is being worked in with the path
Expand All @@ -26,7 +27,7 @@ export function getI18nIdentifierFromFilePath(fileName: string, cwd: string) {
(el) => el === 'public' || el === 'server' || el === 'common'
);

const path = relativePathArray.slice(0, pluginNameIndex).join('/');
const path = relativePathArray.slice(0, pluginNameIndex).join('/').replace('x-pack/', '');

const xpackRC = resolve(join(__dirname, '../../../'), 'x-pack/.i18nrc.json');
const rootRC = resolve(join(__dirname, '../../../'), '.i18nrc.json');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ export function getI18nImportFixer({

// If the file doesn't have an import line for the translation package yet, we need to add it.
// Pretty safe bet to add it underneath the import line for React.
let lineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'") || l.includes('*/'));
let lineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'"));

if (lineIndex === -1) {
lineIndex = sourceCode.lines.findIndex((l) => l.includes('*/'));
}

if (lineIndex === -1) {
lineIndex = 0;
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-eslint-plugin-i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import { StringsShouldBeTranslatedWithI18n } from './rules/strings_should_be_translated_with_i18n';
import { StringsShouldBeTranslatedWithFormattedMessage } from './rules/strings_should_be_translated_with_formatted_message';
import { I18nTranslateShouldStartWithTheRightId } from './rules/i18n_translate_should_start_with_the_right_id';
import { FormattedMessageShouldStartWithTheRightId } from './rules/formatted_message_should_start_with_the_right_id';

/**
* Custom ESLint rules, add `'@kbn/eslint-plugin-i18n'` to your eslint config to use them
Expand All @@ -19,4 +20,5 @@ export const rules = {
strings_should_be_translated_with_formatted_message:
StringsShouldBeTranslatedWithFormattedMessage,
i18n_translate_should_start_with_the_right_id: I18nTranslateShouldStartWithTheRightId,
formatted_message_should_start_with_the_right_id: FormattedMessageShouldStartWithTheRightId,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { RuleTester } from 'eslint';
import {
FormattedMessageShouldStartWithTheRightId,
RULE_WARNING_MESSAGE,
} from './formatted_message_should_start_with_the_right_id';

const tsTester = [
'@typescript-eslint/parser',
new RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
ecmaFeatures: {
jsx: true,
},
},
}),
] as const;

const babelTester = [
'@babel/eslint-parser',
new RuleTester({
parser: require.resolve('@babel/eslint-parser'),
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
requireConfigFile: false,
babelOptions: {
presets: ['@kbn/babel-preset/node_preset'],
},
},
}),
] as const;

for (const [name, tester] of [tsTester, babelTester]) {
describe(name, () => {
tester.run(
'@kbn/formatted_message_should_start_with_the_right_id',
FormattedMessageShouldStartWithTheRightId,
{
valid: [
{
name: 'When a string literal is passed to FormattedMessage the ID attribute should start with the correct i18n identifier, and if no existing defaultMessage is passed, it should add an empty default.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `<FormattedMessage id="xpack.observability." defaultMessage="" />`,
},
{
name: 'When a string literal is passed to FormattedMessage the ID attribute should start with the correct i18n identifier, and if an existing id and defaultMessage is passed, it should leave them alone.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `<FormattedMessage id="xpack.observability.testComponent" defaultMessage="foo" />`,
},
],
invalid: [
{
name: 'When a string literal is passed to FormattedMessage the ID attribute should start with the correct i18n identifier, and if no existing defaultMessage is passed, it should add an empty default.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="foo.bar.baz" />;
}`,
errors: [
{
line: 5,
message: RULE_WARNING_MESSAGE,
},
],
output: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="xpack.observability.bar.baz" defaultMessage="" />;
}`,
},
{
name: 'When a string literal is passed to the ID attribute of <FormattedMessage /> and the root of the i18n identifier is not correct, it should keep the existing identifier but only update the right base app, and keep the default message if available.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="foo.bar.baz" defaultMessage="giraffe" />;
}`,
errors: [
{
line: 5,
message: RULE_WARNING_MESSAGE,
},
],
output: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="xpack.observability.bar.baz" defaultMessage="giraffe" />;
}`,
},
{
name: 'When no string literal is passed to the ID attribute of <FormattedMessage /> it should start with the correct i18n identifier.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage />;
}`,
errors: [
{
line: 5,
message: RULE_WARNING_MESSAGE,
},
],
output: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="xpack.observability.testComponent." defaultMessage="" />;
}`,
},
{
name: 'When i18n is not imported yet, the rule should add it.',
filename:
'/x-pack/plugins/observability_solution/observability/public/test_component.tsx',
code: `
function TestComponent() {
return <FormattedMessage />;
}`,
errors: [
{
line: 3,
message: RULE_WARNING_MESSAGE,
},
],
output: `
import { FormattedMessage } from '@kbn/i18n-react';
function TestComponent() {
return <FormattedMessage id="xpack.observability.testComponent." defaultMessage="" />;
}`,
},
],
}
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { TSESTree } from '@typescript-eslint/typescript-estree';
import type { Rule } from 'eslint';
import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path';
import { getFunctionName } from '../helpers/get_function_name';
import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer';
import { isTruthy } from '../helpers/utils';

export const RULE_WARNING_MESSAGE =
'Id parameter passed to FormattedMessage should start with the correct i18n identifier for this file. Correct it or use the autofix suggestion.';

export const FormattedMessageShouldStartWithTheRightId: Rule.RuleModule = {
meta: {
type: 'suggestion',
fixable: 'code',
},
create(context) {
const { cwd, filename, getScope, sourceCode, report } = context;

return {
JSXElement: (node: TSESTree.JSXElement) => {
const { openingElement } = node;

if (!('name' in openingElement.name) || openingElement.name.name !== 'FormattedMessage') {
return;
}

const idAttribute = openingElement.attributes.find(
(attribute) => 'name' in attribute && attribute.name.name === 'id'
) as TSESTree.JSXAttribute;

const identifier =
idAttribute &&
'value' in idAttribute &&
idAttribute.value &&
'value' in idAttribute.value &&
typeof idAttribute.value.value === 'string' &&
idAttribute.value.value;

const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd);
const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration;
const functionName = getFunctionName(functionDeclaration);

// Check if i18n has already been imported into the file
const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, replaceMode } =
getI18nImportFixer({
sourceCode,
translationFunction: 'FormattedMessage',
});

if (!identifier) {
report({
node: node as any,
message: RULE_WARNING_MESSAGE,
fix(fixer) {
return [
fixer.replaceTextRange(
node.range,
`<FormattedMessage id="${i18nAppId}.${functionName}." defaultMessage="" />`
),
!hasI18nImportLine && rangeToAddI18nImportLine
? replaceMode === 'replace'
? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine)
: fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`)
: null,
].filter(isTruthy);
},
});
}

if (identifier && !identifier.startsWith(`${i18nAppId}.`)) {
const oldI18nIdentifierArray = identifier.split('.');
const newI18nIdentifier =
oldI18nIdentifierArray[0] === 'xpack'
? `${i18nAppId}.${oldI18nIdentifierArray.slice(2).join('.')}`
: `${i18nAppId}.${oldI18nIdentifierArray.slice(1).join('.')}`;

const defaultMessageAttribute = openingElement.attributes.find(
(attribute) => 'name' in attribute && attribute.name.name === 'defaultMessage'
) as TSESTree.JSXAttribute;

const defaultMessage =
(defaultMessageAttribute &&
'value' in defaultMessageAttribute &&
'value' &&
defaultMessageAttribute.value &&
'value' in defaultMessageAttribute.value &&
typeof defaultMessageAttribute.value.value === 'string' &&
defaultMessageAttribute.value.value) ||
'';

report({
node: node as any,
message: RULE_WARNING_MESSAGE,
fix(fixer) {
return [
fixer.replaceTextRange(
node.range,
`<FormattedMessage id="${newI18nIdentifier}" defaultMessage="${defaultMessage}" />`
),
!hasI18nImportLine && rangeToAddI18nImportLine
? replaceMode === 'replace'
? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine)
: fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`)
: null,
].filter(isTruthy);
},
});
}
},
} as Rule.RuleListener;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ export type AssistantFeatures = { [K in keyof typeof defaultAssistantFeatures]:
*/
export const defaultAssistantFeatures = Object.freeze({
assistantModelEvaluation: false,
assistantStreamingEnabled: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,4 @@ import { z } from 'zod';
export type GetCapabilitiesResponse = z.infer<typeof GetCapabilitiesResponse>;
export const GetCapabilitiesResponse = z.object({
assistantModelEvaluation: z.boolean(),
assistantStreamingEnabled: z.boolean(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@ paths:
properties:
assistantModelEvaluation:
type: boolean
assistantStreamingEnabled:
type: boolean
required:
- assistantModelEvaluation
- assistantStreamingEnabled
'400':
description: Generic Error
content:
Expand Down
Loading

0 comments on commit d592c79

Please sign in to comment.