Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose decoded cloudId components from the cloud plugin's contract #159442

Merged
merged 18 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
* 2.0.
*/

export interface DecodedCloudId {
host: string;
defaultPort: string;
elasticsearchUrl: string;
kibanaUrl: string;
}

// decodeCloudId decodes the c.id into c.esURL and c.kibURL
export function decodeCloudId(cid: string):
| {
host: string;
defaultPort: string;
elasticsearchUrl: string;
kibanaUrl: string;
}
| undefined {
export function decodeCloudId(cid: string): DecodedCloudId | undefined {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved, pretty much without any modifications, to the cloud plugin. Tell me if you think anything within the method should be changed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't add a comment in that line. Should we use a different logger to replace all console.debug entries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, we have the client-side loggers now! will do.

// 1. Ignore anything before `:`.
const id = cid.split(':').pop();
if (!id) {
Expand Down Expand Up @@ -56,6 +56,7 @@ export function decodeCloudId(cid: string):
kibanaUrl: kbUrl,
};
}

// extractPortFromName takes a string in the form `id:port` and returns the
// Id and the port. If there's no `:`, the default port is returned
function extractPortFromName(word: string, defaultPort = '443') {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/cloud/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import { PluginInitializerContext } from '@kbn/core/public';
import { CloudPlugin } from './plugin';

export type { CloudSetup, CloudConfigType, CloudStart } from './plugin';
export type { CloudSetup, CloudStart } from './types';
export type { CloudConfigType } from './plugin';

export function plugin(initializerContext: PluginInitializerContext) {
return new CloudPlugin(initializerContext);
Expand Down
9 changes: 7 additions & 2 deletions x-pack/plugins/cloud/public/mocks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@

import React from 'react';

import { CloudStart } from '.';
import type { CloudSetup, CloudStart } from './types';

function createSetupMock() {
function createSetupMock(): jest.Mocked<CloudSetup> {
return {
cloudId: 'mock-cloud-id',
deploymentId: 'mock-deployment-id',
isCloudEnabled: true,
cname: 'cname',
baseUrl: 'base-url',
deploymentUrl: 'deployment-url',
profileUrl: 'profile-url',
organizationUrl: 'organization-url',
elasticsearchUrl: 'elasticsearch-url',
kibanaUrl: 'kibana-url',
cloudHost: 'cloud-host',
cloudDefaultPort: '443',
isElasticStaffOwned: true,
trialEndDate: new Date('2020-10-01T14:13:12Z'),
registerCloudService: jest.fn(),
Expand Down
22 changes: 22 additions & 0 deletions x-pack/plugins/cloud/public/plugin.test.mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const parseDeploymentIdFromDeploymentUrlMock = jest.fn();

jest.doMock('../common/parse_deployment_id_from_deployment_url', () => {
return {
parseDeploymentIdFromDeploymentUrl: parseDeploymentIdFromDeploymentUrlMock,
};
});

export const decodeCloudIdMock = jest.fn();

jest.doMock('../common/decode_cloud_id', () => {
return {
decodeCloudId: decodeCloudIdMock,
};
});
38 changes: 38 additions & 0 deletions x-pack/plugins/cloud/public/plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
* 2.0.
*/

import { decodeCloudIdMock, parseDeploymentIdFromDeploymentUrlMock } from './plugin.test.mocks';
import { coreMock } from '@kbn/core/public/mocks';
import { CloudPlugin } from './plugin';
import type { DecodedCloudId } from '../common/decode_cloud_id';

const baseConfig = {
base_url: 'https://cloud.elastic.co',
Expand All @@ -16,6 +18,11 @@ const baseConfig = {
};

describe('Cloud Plugin', () => {
beforeEach(() => {
parseDeploymentIdFromDeploymentUrlMock.mockReset().mockReturnValue('deployment-id');
decodeCloudIdMock.mockReset().mockReturnValue({});
});

describe('#setup', () => {
describe('interface', () => {
const setupPlugin = () => {
Expand Down Expand Up @@ -76,6 +83,37 @@ describe('Cloud Plugin', () => {
const { setup } = setupPlugin();
expect(setup.registerCloudService).toBeDefined();
});

it('exposes deploymentId', () => {
parseDeploymentIdFromDeploymentUrlMock.mockReturnValue('some-deployment-id');
const { setup } = setupPlugin();
expect(setup.deploymentId).toBe('some-deployment-id');
expect(parseDeploymentIdFromDeploymentUrlMock).toHaveBeenCalledTimes(2); // called when registering the analytic context too
expect(parseDeploymentIdFromDeploymentUrlMock).toHaveBeenCalledWith(
baseConfig.deployment_url
);
});

it('exposes components decoded from the cloudId', () => {
const decodedId: DecodedCloudId = {
defaultPort: '9000',
host: 'host',
elasticsearchUrl: 'elasticsearch-url',
kibanaUrl: 'kibana-url',
};
decodeCloudIdMock.mockReturnValue(decodedId);
const { setup } = setupPlugin();
expect(setup).toEqual(
expect.objectContaining({
cloudDefaultPort: '9000',
cloudHost: 'host',
elasticsearchUrl: 'elasticsearch-url',
kibanaUrl: 'kibana-url',
})
);
expect(decodeCloudIdMock).toHaveBeenCalledTimes(1);
expect(decodeCloudIdMock).toHaveBeenCalledWith('cloudId');
});
});
});

Expand Down
89 changes: 13 additions & 76 deletions x-pack/plugins/cloud/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

import React, { FC } from 'react';
import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';

import { registerCloudDeploymentMetadataAnalyticsContext } from '../common/register_cloud_deployment_id_analytics_context';
import { getIsCloudEnabled } from '../common/is_cloud_enabled';
import { parseDeploymentIdFromDeploymentUrl } from '../common/parse_deployment_id_from_deployment_url';
import { ELASTIC_SUPPORT_LINK, CLOUD_SNAPSHOTS_PATH } from '../common/constants';
import { decodeCloudId, type DecodedCloudId } from '../common/decode_cloud_id';
import type { CloudSetup, CloudStart } from './types';
import { getFullCloudUrl } from './utils';

export interface CloudConfigType {
Expand All @@ -24,81 +26,6 @@ export interface CloudConfigType {
is_elastic_staff_owned?: boolean;
}

export interface CloudStart {
/**
* A React component that provides a pre-wired `React.Context` which connects components to Cloud services.
*/
CloudContextProvider: FC<{}>;
/**
* `true` when Kibana is running on Elastic Cloud.
*/
isCloudEnabled: boolean;
/**
* Cloud ID. Undefined if not running on Cloud.
*/
cloudId?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
*/
deploymentUrl?: string;
/**
* The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud.
*/
profileUrl?: string;
/**
* The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud.
*/
organizationUrl?: string;
}

export interface CloudSetup {
/**
* Cloud ID. Undefined if not running on Cloud.
*/
cloudId?: string;
/**
* This value is the same as `baseUrl` on ESS but can be customized on ECE.
*/
cname?: string;
/**
* This is the URL of the Cloud interface.
*/
baseUrl?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
*/
deploymentUrl?: string;
/**
* The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud.
*/
profileUrl?: string;
/**
* The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud.
*/
organizationUrl?: string;
/**
* This is the path to the Snapshots page for the deployment to which the Kibana instance belongs. The value is already prepended with `deploymentUrl`.
*/
snapshotsUrl?: string;
/**
* `true` when Kibana is running on Elastic Cloud.
*/
isCloudEnabled: boolean;
/**
* When the Cloud Trial ends/ended for the organization that owns this deployment. Only available when running on Elastic Cloud.
*/
trialEndDate?: Date;
/**
* `true` if the Elastic Cloud organization that owns this deployment is owned by an Elastician. Only available when running on Elastic Cloud.
*/
isElasticStaffOwned?: boolean;
/**
* Registers CloudServiceProviders so start's `CloudContextProvider` hooks them.
* @param contextProvider The React component from the Service Provider.
*/
registerCloudService: (contextProvider: FC) => void;
}

interface CloudUrls {
deploymentUrl?: string;
profileUrl?: string;
Expand Down Expand Up @@ -127,11 +54,21 @@ export class CloudPlugin implements Plugin<CloudSetup> {
is_elastic_staff_owned: isElasticStaffOwned,
} = this.config;

let decodedId: DecodedCloudId | undefined;
if (id) {
decodedId = decodeCloudId(id);
}

return {
cloudId: id,
deploymentId: parseDeploymentIdFromDeploymentUrl(this.config.deployment_url),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the deploymentId (which was only exposed on the server-side contract) while I was at it, given some plugins recoded their helper to access it.

cname,
baseUrl,
...this.getCloudUrls(),
elasticsearchUrl: decodedId?.elasticsearchUrl,
kibanaUrl: decodedId?.kibanaUrl,
cloudHost: decodedId?.host,
cloudDefaultPort: decodedId?.defaultPort,
trialEndDate: trialEndDate ? new Date(trialEndDate) : undefined,
isElasticStaffOwned,
isCloudEnabled: this.isCloudEnabled,
Expand Down
111 changes: 111 additions & 0 deletions x-pack/plugins/cloud/public/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { FC } from 'react';

export interface CloudStart {
/**
* A React component that provides a pre-wired `React.Context` which connects components to Cloud services.
*/
CloudContextProvider: FC<{}>;
Comment on lines +10 to +14
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took this opportunity to move the browser-side contracts to a dedicated file, given they started to become quite big.

/**
* `true` when Kibana is running on Elastic Cloud.
*/
isCloudEnabled: boolean;
/**
* Cloud ID. Undefined if not running on Cloud.
*/
cloudId?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
*/
deploymentUrl?: string;
/**
* The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud.
*/
profileUrl?: string;
/**
* The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud.
*/
organizationUrl?: string;
/**
* The full URL to the elasticsearch cluster.
*/
elasticsearchUrl?: string;
/**
* The full URL to the Kibana deployment.
*/
kibanaUrl?: string;
Comment on lines +40 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, is there a difference between this and server.publicBaseUrl? (Other than the fact that this will only be available in a cloud environment?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is. server.publicBaseUrl is an arbitrary URL eventually set by the user, that is supposed to reflect the "public" way of accessing Kibana.

The url decrypted from the cloudId is

  1. only available on cloud (obviously)
  2. reflecting the "internal" way of accessing the deployment.

}

export interface CloudSetup {
/**
* Cloud ID. Undefined if not running on Cloud.
*/
cloudId?: string;
/**
* The deployment's ID. Only available when running on Elastic Cloud.
*/
deploymentId?: string;
/**
* This value is the same as `baseUrl` on ESS but can be customized on ECE.
*/
cname?: string;
/**
* This is the URL of the Cloud interface.
*/
baseUrl?: string;
/**
* The full URL to the deployment management page on Elastic Cloud. Undefined if not running on Cloud.
*/
deploymentUrl?: string;
/**
* The full URL to the user profile page on Elastic Cloud. Undefined if not running on Cloud.
*/
profileUrl?: string;
/**
* The full URL to the organization management page on Elastic Cloud. Undefined if not running on Cloud.
*/
organizationUrl?: string;
/**
* This is the path to the Snapshots page for the deployment to which the Kibana instance belongs. The value is already prepended with `deploymentUrl`.
*/
snapshotsUrl?: string;
/**
* The full URL to the elasticsearch cluster.
*/
elasticsearchUrl?: string;
/**
* The full URL to the Kibana deployment.
*/
kibanaUrl?: string;
/**
* {host} from the deployment url https://<deploymentId>.<application>.<host><?:port>
*/
cloudHost?: string;
/**
* {port} from the deployment url https://<deploymentId>.<application>.<host><?:port>
*/
cloudDefaultPort?: string;
Comment on lines +78 to +93
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming is hard. I went with those, but I'm open to better suggestions (or better TSDoc)

/**
* `true` when Kibana is running on Elastic Cloud.
*/
isCloudEnabled: boolean;
/**
* When the Cloud Trial ends/ended for the organization that owns this deployment. Only available when running on Elastic Cloud.
*/
trialEndDate?: Date;
/**
* `true` if the Elastic Cloud organization that owns this deployment is owned by an Elastician. Only available when running on Elastic Cloud.
*/
isElasticStaffOwned?: boolean;
/**
* Registers CloudServiceProviders so start's `CloudContextProvider` hooks them.
* @param contextProvider The React component from the Service Provider.
*/
registerCloudService: (contextProvider: FC) => void;
}
Loading