diff --git a/docs/settings/alert-action-settings.asciidoc b/docs/settings/alert-action-settings.asciidoc index 4ccb2ddababb5..8d3499234ff28 100644 --- a/docs/settings/alert-action-settings.asciidoc +++ b/docs/settings/alert-action-settings.asciidoc @@ -491,7 +491,7 @@ For an <>, specifies the AWS access key `xpack.actions.preconfigured..secrets.apikey`:: An API key secret that varies by connector: -`xpack.actions.preconfigured..secrets.credentialsJSON`:: +`xpack.actions.preconfigured..secrets.credentialsJson`:: For an <>, specifies the GCP service account credentials JSON file for authentication. + -- diff --git a/x-pack/plugins/actions/docs/openapi/components/schemas/secrets_properties_gemini.yaml b/x-pack/plugins/actions/docs/openapi/components/schemas/secrets_properties_gemini.yaml index 98e89e1ba373c..01c02b5f01abf 100644 --- a/x-pack/plugins/actions/docs/openapi/components/schemas/secrets_properties_gemini.yaml +++ b/x-pack/plugins/actions/docs/openapi/components/schemas/secrets_properties_gemini.yaml @@ -2,8 +2,8 @@ title: Connector secrets properties for a Google Gemini connector description: Defines secrets for connectors when type is `.gemini`. type: object required: - - credentialsJSON + - credentialsJson properties: - credentialsJSON: + credentialsJson: type: string - description: The service account credentials JSON file. The service account should have Vertex AI user IAM role assigned to it. \ No newline at end of file + description: The service account credentials JSON file. The service account should have Vertex AI user IAM role assigned to it. diff --git a/x-pack/plugins/stack_connectors/public/connector_types/gemini/connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/gemini/connector.test.tsx index 55e7385926ad1..0331220cbb363 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/gemini/connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/gemini/connector.test.tsx @@ -37,7 +37,7 @@ const geminiConnector = { gcpProjectID: 'test-project', }, secrets: { - credentialsJSON: JSON.stringify({ + credentialsJson: JSON.stringify({ type: 'service_account', project_id: '', private_key_id: '', @@ -83,6 +83,10 @@ describe('GeminiConnectorFields renders', () => { ); expect(getAllByTestId('gemini-api-doc')[0]).toBeInTheDocument(); expect(getAllByTestId('gemini-api-model-doc')[0]).toBeInTheDocument(); + + expect(getAllByTestId('secrets.credentialsJson-input')[0]).toHaveValue( + geminiConnector.secrets.credentialsJson + ); }); describe('Dashboard link', () => { diff --git a/x-pack/plugins/stack_connectors/public/connector_types/gemini/params.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/gemini/params.test.tsx index b8e3b3029b284..1669603218c50 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/gemini/params.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/gemini/params.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { fireEvent, render } from '@testing-library/react'; import GeminiParamsFields from './params'; -import { DEFAULT_GEMINI_URL, SUB_ACTION } from '../../../common/gemini/constants'; +import { SUB_ACTION } from '../../../common/gemini/constants'; import { I18nProvider } from '@kbn/i18n-react'; const messageVariables = [ @@ -48,37 +48,9 @@ describe('Gemini Params Fields renders', () => { }; const editAction = jest.fn(); const errors = {}; - const actionConnector = { - secrets: { - credentialsJSON: JSON.stringify({ - type: 'service_account', - project_id: '', - private_key_id: '', - private_key: '-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----\n', - client_email: '', - client_id: '', - auth_uri: 'https://accounts.google.com/o/oauth2/auth', - token_uri: 'https://oauth2.googleapis.com/token', - auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs', - client_x509_cert_url: '', - }), - }, - id: 'test', - actionTypeId: '.gemini', - isPreconfigured: false, - isSystemAction: false as const, - isDeprecated: false, - name: 'My Gemini Connector', - config: { - apiUrl: DEFAULT_GEMINI_URL, - gcpRegion: 'us-central-1', - gcpProjectID: 'test-project', - }, - }; render( { /** Retrieve access token based on the GCP service account credential json file */ private async getAccessToken(): Promise { // Validate the service account credentials JSON file input - let credentialsJSON; + let credentialsJson; try { - credentialsJSON = JSON.parse(this.secrets.credentialsJson); + credentialsJson = JSON.parse(this.secrets.credentialsJson); } catch (error) { throw new Error(`Failed to parse credentials JSON file: Invalid JSON format`); } const accessToken = await getGoogleOAuthJwtAccessToken({ connectorId: this.connector.id, logger: this.logger, - credentials: credentialsJSON, + credentials: credentialsJson, connectorTokenClient: this.connectorTokenClient, }); return accessToken; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ba7f8e76e2c98..354006de80dbf 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -43573,7 +43573,7 @@ "xpack.stackConnectors.components.gemini.bodyCodeEditorAriaLabel": "Éditeur de code", "xpack.stackConnectors.components.gemini.bodyFieldLabel": "Corps", "xpack.stackConnectors.components.gemini.connectorTypeTitle": "Google Gemini", - "xpack.stackConnectors.components.gemini.credentialsJSON": "Informations d'identification JSON", + "xpack.stackConnectors.components.gemini.credentialsJson": "Informations d'identification JSON", "xpack.stackConnectors.components.gemini.dashboardLink": "Affichez le tableau de bord d'utilisation de {apiProvider} pour le connecteur \"{ connectorName }\"", "xpack.stackConnectors.components.gemini.defaultModelTextFieldLabel": "Modèle par défaut", "xpack.stackConnectors.components.gemini.documentation": "documentation", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5399d680ebd81..bdf07ef5a659f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -43550,7 +43550,7 @@ "xpack.stackConnectors.components.gemini.bodyCodeEditorAriaLabel": "コードエディター", "xpack.stackConnectors.components.gemini.bodyFieldLabel": "本文", "xpack.stackConnectors.components.gemini.connectorTypeTitle": "Google Gemini", - "xpack.stackConnectors.components.gemini.credentialsJSON": "資格情報JSON", + "xpack.stackConnectors.components.gemini.credentialsJson": "資格情報JSON", "xpack.stackConnectors.components.gemini.dashboardLink": "\"{ connectorName }\"コネクターの{apiProvider}使用状況ダッシュボードを表示", "xpack.stackConnectors.components.gemini.defaultModelTextFieldLabel": "デフォルトモデル", "xpack.stackConnectors.components.gemini.documentation": "ドキュメンテーション", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fcec9d85c1207..dd7fb48a244a4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -43600,7 +43600,7 @@ "xpack.stackConnectors.components.gemini.bodyCodeEditorAriaLabel": "代码编辑器", "xpack.stackConnectors.components.gemini.bodyFieldLabel": "正文", "xpack.stackConnectors.components.gemini.connectorTypeTitle": "Google Gemini", - "xpack.stackConnectors.components.gemini.credentialsJSON": "凭据 JSON", + "xpack.stackConnectors.components.gemini.credentialsJson": "凭据 JSON", "xpack.stackConnectors.components.gemini.dashboardLink": "查看“{ connectorName }”连接器的 {apiProvider} 使用情况仪表板", "xpack.stackConnectors.components.gemini.defaultModelTextFieldLabel": "默认模型", "xpack.stackConnectors.components.gemini.documentation": "文档", diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 2b4f8e2cd8aff..354b1bdcc2ac7 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -40,6 +40,7 @@ const enabledActionTypes = [ '.bedrock', '.cases-webhook', '.email', + '.gemini', '.index', '.opsgenie', '.pagerduty', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts index 54eebf207e7d7..8d235c15dc21c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/gemini.ts @@ -11,6 +11,7 @@ import { geminiSuccessResponse, } from '@kbn/actions-simulators-plugin/server/gemini_simulation'; import { TaskErrorSource } from '@kbn/task-manager-plugin/common'; +import { DEFAULT_GEMINI_MODEL } from '@kbn/stack-connectors-plugin/common/gemini/constants'; import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; const connectorTypeId = '.gemini'; @@ -20,7 +21,7 @@ const defaultConfig = { gcpProjectID: 'test-project', }; const secrets = { - credentialsJSON: JSON.stringify({ + credentialsJson: JSON.stringify({ type: 'service_account', project_id: '', private_key_id: '', @@ -39,14 +40,14 @@ export default function geminiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const configService = getService('config'); - const createConnector = async (url: string) => { + const createConnector = async (apiUrl: string) => { const { body } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name, connector_type_id: connectorTypeId, - config: { ...defaultConfig, url }, + config: { ...defaultConfig, apiUrl }, secrets, }) .expect(200); @@ -62,10 +63,10 @@ export default function geminiTest({ getService }: FtrProviderContext) { config: configService.get('kbnTestServer.serverArgs'), }, }); - const config = { ...defaultConfig, url: '' }; + const config = { ...defaultConfig, apiUrl: '' }; before(async () => { - config.url = await simulator.start(); + config.apiUrl = await simulator.start(); }); after(() => { @@ -92,7 +93,10 @@ export default function geminiTest({ getService }: FtrProviderContext) { name, connector_type_id: connectorTypeId, is_missing_secrets: false, - config, + config: { + ...config, + defaultModel: DEFAULT_GEMINI_MODEL, + }, }); }); @@ -112,20 +116,20 @@ export default function geminiTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type config: [url, gcpRegion, gcpProjectID]: expected value of type [string] but got [undefined]', + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', }); }); }); it('should return 400 Bad Request when creating the connector without the project id', async () => { - const testConfig = { gcpRegion: 'us-central-1', url: '' }; + const testConfig = { gcpRegion: 'us-central-1', apiUrl: 'https://url.co' }; await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name, connector_type_id: connectorTypeId, - testConfig, + config: testConfig, secrets, }) .expect(400) @@ -140,14 +144,14 @@ export default function geminiTest({ getService }: FtrProviderContext) { }); it('should return 400 Bad Request when creating the connector without the region', async () => { - const testConfig = { gcpProjectID: 'test-project', url: '' }; + const testConfig = { gcpProjectID: 'test-project', apiUrl: 'https://url.co' }; await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') .send({ name, connector_type_id: connectorTypeId, - testConfig, + config: testConfig, secrets, }) .expect(400) @@ -169,7 +173,8 @@ export default function geminiTest({ getService }: FtrProviderContext) { name, connector_type_id: connectorTypeId, config: { - url: 'http://gemini.mynonexistent.com', + ...defaultConfig, + apiUrl: 'http://gemini.mynonexistent.com', }, secrets, }) @@ -179,7 +184,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type config: error validating url: target url "http://gemini.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + 'error validating action type config: Error configuring Google Gemini action: Error: error validating url: target url "http://gemini.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', }); }); }); @@ -199,7 +204,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type secrets: [token]: expected value of type [string] but got [undefined]', + 'error validating action type secrets: [credentialsJson]: expected value of type [string] but got [undefined]', }); }); }); @@ -257,7 +262,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { retry: true, message: 'an error occurred while running the action', errorSource: TaskErrorSource.FRAMEWORK, - service_message: `Sub action "invalidAction" is not registered. Connector id: ${geminiActionId}. Connector name: Gemini. Connector type: .gemini`, + service_message: `Sub action "invalidAction" is not registered. Connector id: ${geminiActionId}. Connector name: Google Gemini. Connector type: .gemini`, }); }); }); @@ -269,19 +274,21 @@ export default function geminiTest({ getService }: FtrProviderContext) { config: configService.get('kbnTestServer.serverArgs'), }, }); - let url: string; + let apiUrl: string; let geminiActionId: string; before(async () => { - url = await simulator.start(); - geminiActionId = await createConnector(url); + apiUrl = await simulator.start(); + geminiActionId = await createConnector(apiUrl); }); after(() => { simulator.close(); }); - it('should invoke AI with assistant AI body argument formatted to gemini expectations', async () => { + // TODO to fix, we need to figure out how to mock the gcp oauth token + // https://github.com/elastic/kibana/issues/195437 + it.skip('should invoke AI with assistant AI body argument formatted to gemini expectations', async () => { const { body } = await supertest .post(`/api/actions/connector/${geminiActionId}/_execute`) .set('kbn-xsrf', 'foo') @@ -289,7 +296,7 @@ export default function geminiTest({ getService }: FtrProviderContext) { params: { subAction: 'invokeAI', subActionParams: { - contents: [ + messages: [ { role: 'user', parts: [ @@ -315,7 +322,6 @@ export default function geminiTest({ getService }: FtrProviderContext) { ], }, ], - generation_config: { temperature: 0, maxOutputTokens: 8192 }, }, }, }) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts index ab79a189839e2..beb16133241e1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/index.ts @@ -44,6 +44,7 @@ export default function connectorsTests({ loadTestFile, getService }: FtrProvide loadTestFile(require.resolve('./connector_types/openai')); loadTestFile(require.resolve('./connector_types/d3security')); loadTestFile(require.resolve('./connector_types/bedrock')); + loadTestFile(require.resolve('./connector_types/gemini')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./execute'));