Skip to content

Commit

Permalink
[Cases] Migrate user actions connector ID (#108272)
Browse files Browse the repository at this point in the history
* Making progress

* Fleshing out the extraction logic

* Finishing migration logic and starting more tests

* Finishing migration unit tests

* Making progress on services

* Finishing transform to es schema

* Finishing transform functionality and unit tests

* reverting migration data updates

* Cleaning up type errors

* fixing test error

* Working migration tests

* Refactoring retrieval of connector fields

* Refactoring connector id in and tests in frontend

* Fixing tests and finished refactoring parse string

* Fixing integration test

* Fixing integration tests

* Removing some duplicate code and updating test name

* Fixing create connector user action bug

* Addressing feedback and logging error

* Moving parsing function to common

* Fixing type errors

* Fixing type errors

* Addressing feedback

* Fixing lint errors

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
jonathan-buttner and kibanamachine authored Sep 20, 2021
1 parent 6f2815a commit 10ac814
Show file tree
Hide file tree
Showing 57 changed files with 7,895 additions and 1,140 deletions.
16 changes: 13 additions & 3 deletions x-pack/plugins/cases/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ const CaseBasicRt = rt.type({
owner: rt.string,
});

export const CaseExternalServiceBasicRt = rt.type({
connector_id: rt.union([rt.string, rt.null]),
/**
* This represents the push to service UserAction. It lacks the connector_id because that is stored in a different field
* within the user action object in the API response.
*/
export const CaseUserActionExternalServiceRt = rt.type({
connector_name: rt.string,
external_id: rt.string,
external_title: rt.string,
Expand All @@ -97,7 +100,14 @@ export const CaseExternalServiceBasicRt = rt.type({
pushed_by: UserRT,
});

const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]);
export const CaseExternalServiceBasicRt = rt.intersection([
rt.type({
connector_id: rt.union([rt.string, rt.null]),
}),
CaseUserActionExternalServiceRt,
]);

export const CaseFullExternalServiceRt = rt.union([CaseExternalServiceBasicRt, rt.null]);

export const CaseAttributesRt = rt.intersection([
CaseBasicRt,
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/cases/common/api/cases/user_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const UserActionRt = rt.union([
rt.literal('push-to-service'),
]);

// TO DO change state to status
const CaseUserActionBasicRT = rt.type({
action_field: UserActionFieldRt,
action: UserActionRt,
Expand All @@ -51,6 +50,8 @@ const CaseUserActionResponseRT = rt.intersection([
action_id: rt.string,
case_id: rt.string,
comment_id: rt.union([rt.string, rt.null]),
new_val_connector_id: rt.union([rt.string, rt.null]),
old_val_connector_id: rt.union([rt.string, rt.null]),
}),
rt.partial({ sub_case_id: rt.string }),
]);
Expand Down
12 changes: 10 additions & 2 deletions x-pack/plugins/cases/common/api/connectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,22 @@ export const ConnectorTypeFieldsRt = rt.union([
ConnectorSwimlaneTypeFieldsRt,
]);

/**
* This type represents the connector's format when it is encoded within a user action.
*/
export const CaseUserActionConnectorRt = rt.intersection([
rt.type({ name: rt.string }),
ConnectorTypeFieldsRt,
]);

export const CaseConnectorRt = rt.intersection([
rt.type({
id: rt.string,
name: rt.string,
}),
ConnectorTypeFieldsRt,
CaseUserActionConnectorRt,
]);

export type CaseUserActionConnector = rt.TypeOf<typeof CaseUserActionConnectorRt>;
export type CaseConnector = rt.TypeOf<typeof CaseConnectorRt>;
export type ConnectorTypeFields = rt.TypeOf<typeof ConnectorTypeFieldsRt>;
export type ConnectorJiraTypeFields = rt.TypeOf<typeof ConnectorJiraTypeFieldsRt>;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/cases/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './constants';
export * from './api';
export * from './ui/types';
export * from './utils/connectors_api';
export * from './utils/user_actions';
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/common/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ export interface CaseUserActions {
caseId: string;
commentId: string | null;
newValue: string | null;
newValConnectorId: string | null;
oldValue: string | null;
oldValConnectorId: string | null;
}

export interface CaseExternalService {
Expand Down
18 changes: 18 additions & 0 deletions x-pack/plugins/cases/common/utils/user_actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 function isCreateConnector(action?: string, actionFields?: string[]): boolean {
return action === 'create' && actionFields != null && actionFields.includes('connector');
}

export function isUpdateConnector(action?: string, actionFields?: string[]): boolean {
return action === 'update' && actionFields != null && actionFields.includes('connector');
}

export function isPush(action?: string, actionFields?: string[]): boolean {
return action === 'push-to-service' && actionFields != null && actionFields.includes('pushed');
}
8 changes: 8 additions & 0 deletions x-pack/plugins/cases/public/common/user_actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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 * from './parsers';
86 changes: 86 additions & 0 deletions x-pack/plugins/cases/public/common/user_actions/parsers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 { ConnectorTypes, noneConnectorId } from '../../../common';
import { parseStringAsConnector, parseStringAsExternalService } from './parsers';

describe('user actions utility functions', () => {
describe('parseStringAsConnector', () => {
it('return null if the data is null', () => {
expect(parseStringAsConnector('', null)).toBeNull();
});

it('return null if the data is not a json object', () => {
expect(parseStringAsConnector('', 'blah')).toBeNull();
});

it('return null if the data is not a valid connector', () => {
expect(parseStringAsConnector('', JSON.stringify({ a: '1' }))).toBeNull();
});

it('return null if id is null but the data is a connector other than none', () => {
expect(
parseStringAsConnector(
null,
JSON.stringify({ type: ConnectorTypes.jira, name: '', fields: null })
)
).toBeNull();
});

it('return the id as the none connector if the data is the none connector', () => {
expect(
parseStringAsConnector(
null,
JSON.stringify({ type: ConnectorTypes.none, name: '', fields: null })
)
).toEqual({ id: noneConnectorId, type: ConnectorTypes.none, name: '', fields: null });
});

it('returns a decoded connector with the specified id', () => {
expect(
parseStringAsConnector(
'a',
JSON.stringify({ type: ConnectorTypes.jira, name: 'hi', fields: null })
)
).toEqual({ id: 'a', type: ConnectorTypes.jira, name: 'hi', fields: null });
});
});

describe('parseStringAsExternalService', () => {
it('returns null when the data is null', () => {
expect(parseStringAsExternalService('', null)).toBeNull();
});

it('returns null when the data is not valid json', () => {
expect(parseStringAsExternalService('', 'blah')).toBeNull();
});

it('returns null when the data is not a valid external service object', () => {
expect(parseStringAsExternalService('', JSON.stringify({ a: '1' }))).toBeNull();
});

it('returns the decoded external service with the connector_id field added', () => {
const externalServiceInfo = {
connector_name: 'name',
external_id: '1',
external_title: 'title',
external_url: 'abc',
pushed_at: '1',
pushed_by: {
username: 'a',
email: '[email protected]',
full_name: 'a',
},
};

expect(parseStringAsExternalService('500', JSON.stringify(externalServiceInfo))).toEqual({
...externalServiceInfo,
connector_id: '500',
});
});
});
});
77 changes: 77 additions & 0 deletions x-pack/plugins/cases/public/common/user_actions/parsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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 {
CaseUserActionConnectorRt,
CaseConnector,
ConnectorTypes,
noneConnectorId,
CaseFullExternalService,
CaseUserActionExternalServiceRt,
} from '../../../common';

export const parseStringAsConnector = (
id: string | null,
encodedData: string | null
): CaseConnector | null => {
if (encodedData == null) {
return null;
}

const decodedConnector = parseString(encodedData);

if (!CaseUserActionConnectorRt.is(decodedConnector)) {
return null;
}

if (id == null && decodedConnector.type === ConnectorTypes.none) {
return {
...decodedConnector,
id: noneConnectorId,
};
} else if (id == null) {
return null;
} else {
// id does not equal null or undefined and the connector type does not equal none
// so return the connector with its id
return {
...decodedConnector,
id,
};
}
};

const parseString = (params: string | null): unknown | null => {
if (params == null) {
return null;
}

try {
return JSON.parse(params);
} catch {
return null;
}
};

export const parseStringAsExternalService = (
id: string | null,
encodedData: string | null
): CaseFullExternalService => {
if (encodedData == null) {
return null;
}

const decodedExternalService = parseString(encodedData);
if (!CaseUserActionExternalServiceRt.is(decodedExternalService)) {
return null;
}

return {
...decodedExternalService,
connector_id: id,
};
};
Loading

0 comments on commit 10ac814

Please sign in to comment.