Skip to content

Commit

Permalink
Pass request headers when making application config calls (opensearch…
Browse files Browse the repository at this point in the history
…-project#6164)

Application config plugin allows external plugin to register a different storage other than the default OpenSearch. The external plugin may need some additional information about the request headers when handling the requests.

* Allow extra information in the application config calls

Signed-off-by: Tianle Huang <[email protected]>

* only expose headers

Signed-off-by: Tianle Huang <[email protected]>

* update jsdoc

Signed-off-by: Tianle Huang <[email protected]>

* add more logs

Signed-off-by: Tianle Huang <[email protected]>

* update change log

Signed-off-by: Tianle Huang <[email protected]>

* update readme

Signed-off-by: Tianle Huang <[email protected]>

* update readme

Signed-off-by: Tianle Huang <[email protected]>

---------

Signed-off-by: Tianle Huang <[email protected]>
  • Loading branch information
tianleh authored Mar 19, 2024
1 parent a9e874f commit bfab07c
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Implement new home page ([#6065](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6065))
- Add sidecar service ([#5920](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5920))
- [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244))
- [Dynamic Configurations] Pass request headers when making application config calls ([#6164](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6164))


### 🐛 Bug Fixes
Expand Down
10 changes: 6 additions & 4 deletions src/plugins/application_config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ Let's call this plugin `MyConfigurationClientPlugin`.
First, this plugin will need to implement a class `MyConfigurationClient` based on interface `ConfigurationClient` defined in the `types.ts` under directory `src/plugins/application_config/server/types.ts`. Below are the functions inside the interface.

```
getConfig(): Promise<Map<string, string>>;
getConfig(options?: ConfigurationClientOptions): Promise<Map<string, string>>;
getEntityConfig(entity: string): Promise<string>;
getEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise<string>;
updateEntityConfig(entity: string, newValue: string): Promise<string>;
updateEntityConfig(entity: string, newValue: string, options?: ConfigurationClientOptions): Promise<string>;
deleteEntityConfig(entity: string): Promise<string>;
deleteEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise<string>;
```

`ConfigurationClientOptions` wraps some additional parameters including request headers.

Second, this plugin needs to declare `applicationConfig` as its dependency by adding it to `requiredPlugins` in its own `opensearch_dashboards.json`.

Third, the plugin will define a new type called `AppPluginSetupDependencies` as follows in its own `types.ts`.
Expand Down
8 changes: 6 additions & 2 deletions src/plugins/application_config/server/routes/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ describe('application config routes', () => {
getConfig: jest.fn().mockReturnValue(configurations),
};

const request = {};

const okResponse = {
statusCode: 200,
};
Expand All @@ -89,7 +91,7 @@ describe('application config routes', () => {

const logger = loggerMock.create();

const returnedResponse = await handleGetConfig(client, response, logger);
const returnedResponse = await handleGetConfig(client, request, response, logger);

expect(returnedResponse).toBe(okResponse);

Expand All @@ -109,13 +111,15 @@ describe('application config routes', () => {
}),
};

const request = {};

const response = {
customError: jest.fn().mockReturnValue(ERROR_RESPONSE),
};

const logger = loggerMock.create();

const returnedResponse = await handleGetConfig(client, response, logger);
const returnedResponse = await handleGetConfig(client, request, response, logger);

expect(returnedResponse).toBe(ERROR_RESPONSE);

Expand Down
27 changes: 22 additions & 5 deletions src/plugins/application_config/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function defineRoutes(
async (context, request, response) => {
const client = getConfigurationClient(context.core.opensearch.client);

return await handleGetConfig(client, response, logger);
return await handleGetConfig(client, request, response, logger);
}
);
router.get(
Expand Down Expand Up @@ -85,8 +85,12 @@ export async function handleGetEntityConfig(
response: OpenSearchDashboardsResponseFactory,
logger: Logger
) {
logger.info(`Received a request to get entity config for ${request.params.entity}.`);

try {
const result = await client.getEntityConfig(request.params.entity);
const result = await client.getEntityConfig(request.params.entity, {
headers: request.headers,
});
return response.ok({
body: {
value: result,
Expand All @@ -104,8 +108,14 @@ export async function handleUpdateEntityConfig(
response: OpenSearchDashboardsResponseFactory,
logger: Logger
) {
logger.info(
`Received a request to update entity ${request.params.entity} with new value ${request.body.newValue}.`
);

try {
const result = await client.updateEntityConfig(request.params.entity, request.body.newValue);
const result = await client.updateEntityConfig(request.params.entity, request.body.newValue, {
headers: request.headers,
});
return response.ok({
body: {
newValue: result,
Expand All @@ -123,8 +133,12 @@ export async function handleDeleteEntityConfig(
response: OpenSearchDashboardsResponseFactory,
logger: Logger
) {
logger.info(`Received a request to delete entity ${request.params.entity}.`);

try {
const result = await client.deleteEntityConfig(request.params.entity);
const result = await client.deleteEntityConfig(request.params.entity, {
headers: request.headers,
});
return response.ok({
body: {
deletedEntity: result,
Expand All @@ -138,11 +152,14 @@ export async function handleDeleteEntityConfig(

export async function handleGetConfig(
client: ConfigurationClient,
request: OpenSearchDashboardsRequest,
response: OpenSearchDashboardsResponseFactory,
logger: Logger
) {
logger.info('Received a request to get all configurations.');

try {
const result = await client.getConfig();
const result = await client.getConfig({ headers: request.headers });
return response.ok({
body: {
value: result,
Expand Down
25 changes: 18 additions & 7 deletions src/plugins/application_config/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { IScopedClusterClient } from 'src/core/server';
import { IScopedClusterClient, Headers } from 'src/core/server';

export interface ApplicationConfigPluginSetup {
getConfigurationClient: (inputOpenSearchClient: IScopedClusterClient) => ConfigurationClient;
Expand All @@ -12,6 +12,10 @@ export interface ApplicationConfigPluginSetup {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ApplicationConfigPluginStart {}

export interface ConfigurationClientOptions {
headers: Headers;
}

/**
* The interface defines the operations against the application configurations at both entity level and whole level.
*
Expand All @@ -20,33 +24,40 @@ export interface ConfigurationClient {
/**
* Get all the configurations.
*
* @param {array} array of connections
* @returns {ConnectionPool}
* @param {options} options, an optional parameter
* @returns {Map<string, string>} all the configurations
*/
getConfig(): Promise<Map<string, string>>;
getConfig(options?: ConfigurationClientOptions): Promise<Map<string, string>>;

/**
* Get the value for the input entity.
*
* @param {entity} name of the entity
* @param {options} options, an optional parameter
* @returns {string} value of the entity
*/
getEntityConfig(entity: string): Promise<string>;
getEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise<string>;

/**
* Update the input entity with a new value.
*
* @param {entity} name of the entity
* @param {newValue} new configuration value of the entity
* @param {options} options, an optional parameter
* @returns {string} updated configuration value of the entity
*/
updateEntityConfig(entity: string, newValue: string): Promise<string>;
updateEntityConfig(
entity: string,
newValue: string,
options?: ConfigurationClientOptions
): Promise<string>;

/**
* Delete the input entity from configurations.
*
* @param {entity} name of the entity
* @param {options} options, an optional parameter
* @returns {string} name of the deleted entity
*/
deleteEntityConfig(entity: string): Promise<string>;
deleteEntityConfig(entity: string, options?: ConfigurationClientOptions): Promise<string>;
}
4 changes: 3 additions & 1 deletion src/plugins/csp_handler/server/csp_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export function createCspRulesPreResponseHandler(

const client = getConfigurationClient(coreStart.opensearch.client.asScoped(request));

const cspRules = await client.getEntityConfig(CSP_RULES_CONFIG_KEY);
const cspRules = await client.getEntityConfig(CSP_RULES_CONFIG_KEY, {
headers: request.headers,
});

if (!cspRules) {
return appendFrameAncestorsWhenMissing(cspHeader, toolkit);
Expand Down

0 comments on commit bfab07c

Please sign in to comment.