Skip to content

Commit

Permalink
add more unit test cases
Browse files Browse the repository at this point in the history
Signed-off-by: Hailong Cui <[email protected]>
  • Loading branch information
Hailong-am committed Oct 9, 2023
1 parent 37171cf commit 06b3198
Show file tree
Hide file tree
Showing 9 changed files with 867 additions and 64 deletions.
2 changes: 2 additions & 0 deletions src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ export {
SavedObjectsAddToWorkspacesOptions,
SavedObjectsAddToWorkspacesResponse,
SavedObjectsDeleteByWorkspaceOptions,
SavedObjectsDeleteFromWorkspacesOptions,
SavedObjectsDeleteFromWorkspacesResponse,
Permissions,
ACL,
Principals,
Expand Down
172 changes: 172 additions & 0 deletions src/core/server/saved_objects/routes/integration_tests/share.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/
import supertest from 'supertest';
import { UnwrapPromise } from '@osd/utility-types';
import { registerShareRoute } from '../share';
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
import { createExportableType, setupServer } from '../test_utils';
import { WORKSPACE_TYPE } from '../../../../utils/constants';
import { typeRegistryMock } from '../../saved_objects_type_registry.mock';

type SetupServerReturn = UnwrapPromise<ReturnType<typeof setupServer>>;

describe('POST /api/saved_objects/_share', () => {
let server: SetupServerReturn['server'];
let httpSetup: SetupServerReturn['httpSetup'];
let handlerContext: SetupServerReturn['handlerContext'];
let savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
let typeRegistry: ReturnType<typeof typeRegistryMock.create>;
const allowedTypes = ['index-pattern', 'dashboard', 'settings'];

beforeEach(async () => {
const clientResponse = [
{
id: 'abc123',
type: 'index-pattern',
workspaces: ['ws-1', 'ws-2'],
},
];

const bulkGetResponse = {
saved_objects: [
{
id: 'abc123',
type: 'index-pattern',
title: 'logstash-*',
version: 'foo',
references: [],
attributes: {},
workspaces: ['ws-1'],
},
],
};

({ server, httpSetup, handlerContext } = await setupServer());
typeRegistry = handlerContext.savedObjects.typeRegistry;
typeRegistry.getAllTypes.mockReturnValue(allowedTypes.map(createExportableType));

savedObjectsClient = handlerContext.savedObjects.client;
savedObjectsClient.addToWorkspaces.mockResolvedValue(clientResponse);
savedObjectsClient.bulkGet.mockImplementation(() => Promise.resolve(bulkGetResponse));

const router = httpSetup.createRouter('/api/saved_objects/');
registerShareRoute(router);

await server.start();
});

afterEach(async () => {
await server.stop();
});

it('workspace itself are not allowed to share', async () => {
const result = await supertest(httpSetup.server.listener)
.post('/api/saved_objects/_share')
.send({
objects: [
{
id: 'abc123',
type: WORKSPACE_TYPE,
},
],
targetWorkspaceIds: ['ws-2'],
})
.expect(400);

expect(result.body.message).toEqual(
`Trying to share object(s) with non-shareable types: ${WORKSPACE_TYPE}:abc123`
);
});

it('ignore legacy saved objects when share', async () => {
const bulkGetResponse = {
saved_objects: [
{
id: 'settings-1.0',
type: 'settings',
title: 'Advanced-settings',
version: 'foo',
references: [],
attributes: {},
workspaces: undefined,
},
],
};
savedObjectsClient.bulkGet.mockImplementation(() => Promise.resolve(bulkGetResponse));

const clientResponse = [
{
id: 'settings-1.0',
type: 'settings',
workspaces: undefined,
},
];

const result = await supertest(httpSetup.server.listener)
.post('/api/saved_objects/_share')
.send({
objects: [
{
id: 'settings-1.0',
type: 'settings',
},
],
targetWorkspaceIds: ['ws-2'],
});

expect(result.body).toEqual(clientResponse);
});

it('formats successful response', async () => {
const clientResponse = [
{
id: 'abc123',
type: 'index-pattern',
workspaces: ['ws-1', 'ws-2'],
},
];
const result = await supertest(httpSetup.server.listener)
.post('/api/saved_objects/_share')
.send({
objects: [
{
id: 'abc123',
type: 'index-pattern',
},
],
targetWorkspaceIds: ['ws-2'],
})
.expect(200);

expect(result.body).toEqual(clientResponse);
});

it('calls upon savedObjectClient.addToWorkspaces', async () => {
await supertest(httpSetup.server.listener)
.post('/api/saved_objects/_share')
.send({
objects: [
{
id: 'abc123',
type: 'index-pattern',
},
],
targetWorkspaceIds: ['ws-2'],
})
.expect(200);

expect(savedObjectsClient.addToWorkspaces).toHaveBeenCalledWith(
[
{
id: 'abc123',
type: 'index-pattern',
workspaces: ['ws-1'],
},
],
['ws-2'],
{ workspaces: undefined }
);
});
});
12 changes: 7 additions & 5 deletions src/core/server/saved_objects/routes/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { schema } from '@osd/config-schema';
import { IRouter } from '../../http';
import { exportSavedObjectsToStream } from '../export';
import { validateObjects } from './utils';
import { filterInvalidObjects } from './utils';
import { collectSavedObjects } from '../import/collect_saved_objects';
import { WORKSPACE_TYPE } from '../../../utils';

Expand Down Expand Up @@ -39,12 +39,14 @@ export const registerShareRoute = (router: IRouter) => {
.filter((type) => type.name !== WORKSPACE_TYPE)
.map((t) => t.name);

if (objects) {
const validationError = validateObjects(objects, supportedTypes);
if (validationError) {
if (objects.length) {
const invalidObjects = filterInvalidObjects(objects, supportedTypes);
if (invalidObjects.length) {
return res.badRequest({
body: {
message: validationError,
message: `Trying to share object(s) with non-shareable types: ${invalidObjects
.map((obj) => `${obj.type}:${obj.id}`)
.join(', ')}`,
},
});
}
Expand Down
40 changes: 39 additions & 1 deletion src/core/server/saved_objects/routes/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
* under the License.
*/

import { createSavedObjectsStreamFromNdJson, validateTypes, validateObjects } from './utils';
import {
createSavedObjectsStreamFromNdJson,
validateTypes,
validateObjects,
filterInvalidObjects,
} from './utils';
import { Readable } from 'stream';
import { createPromiseFromStreams, createConcatStream } from '../../utils/streams';

Expand Down Expand Up @@ -165,3 +170,36 @@ describe('validateObjects', () => {
).toBeUndefined();
});
});

describe('filterInvalidObjects', () => {
const allowedTypes = ['config', 'index-pattern', 'dashboard'];

it('returns invalid objects that types are not allowed', () => {
expect(
filterInvalidObjects(
[
{ id: '1', type: 'config' },
{ id: '1', type: 'not-allowed' },
{ id: '42', type: 'not-allowed-either' },
],
allowedTypes
)
).toMatchObject([
{ id: '1', type: 'not-allowed' },
{ id: '42', type: 'not-allowed-either' },
]);
});

it('returns empty array if all objects have allowed types', () => {
expect(
validateObjects(
[
{ id: '1', type: 'config' },
{ id: '2', type: 'config' },
{ id: '1', type: 'index-pattern' },
],
allowedTypes
)
).toEqual(undefined);
});
});
7 changes: 7 additions & 0 deletions src/core/server/saved_objects/routes/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,10 @@ export function validateObjects(
.join(', ')}`;
}
}

export function filterInvalidObjects(
objects: Array<{ id: string; type: string }>,
supportedTypes: string[]
): Array<{ id: string; type: string }> {
return objects.filter((obj) => !supportedTypes.includes(obj.type));
}
Loading

0 comments on commit 06b3198

Please sign in to comment.