ee/app/assets/javascripts/audit_events/graphql/apollo.js (299 lines of code) (raw):
import {
DESTINATION_TYPE_HTTP,
DESTINATION_TYPE_AMAZON_S3,
DESTINATION_TYPE_GCP_LOGGING,
} from '../constants';
import updateGroupStreamingDestination from './mutations/update_group_streaming_destination.mutation.graphql';
import createGroupStreamingDestination from './mutations/create_group_streaming_destination.mutation.graphql';
import addGroupEventTypeFiltersToDestination from './mutations/add_group_event_type_filters.mutation.graphql';
import addGroupNamespaceFiltersToDestination from './mutations/add_group_namespace_filters.mutation.graphql';
import deleteGroupEventTypeFiltersFromDestination from './mutations/delete_group_event_type_filters.mutation.graphql';
import deleteGroupNamespaceFiltersFromDestination from './mutations/delete_group_namespace_filters.mutation.graphql';
import updateInstanceStreamingDestination from './mutations/update_instance_streaming_destination.mutation.graphql';
import createInstanceStreamingDestination from './mutations/create_instance_streaming_destination.mutation.graphql';
import addInstanceEventTypeFiltersToDestination from './mutations/add_instance_event_type_filters.mutation.graphql';
import deleteInstanceEventTypeFiltersFromDestination from './mutations/delete_instance_event_type_filters.mutation.graphql';
import {
addAuditEventsStreamingDestinationToCache,
updateAuditEventsStreamingDestinationFromCache,
updateEventTypeFiltersFromCache,
addNamespaceFilterToCache,
} from './cache_update_consolidated_api';
const getCategoryInGraphqlFormat = (category) => {
switch (category) {
case DESTINATION_TYPE_HTTP:
return 'http';
case DESTINATION_TYPE_GCP_LOGGING:
return 'gcp';
case DESTINATION_TYPE_AMAZON_S3:
return 'aws';
default:
return category;
}
};
const getCreatedEventTypeFiltersData = (data, isInstance) =>
isInstance
? data.auditEventsInstanceDestinationEventsAdd
: data.auditEventsGroupDestinationEventsAdd;
const getCreatedDestinationData = (data, isInstance) =>
isInstance
? data.instanceAuditEventStreamingDestinationsCreate
: data.groupAuditEventStreamingDestinationsCreate;
/**
* Add event type filters to a destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {Array} eventTypeFiltersToAdd - Array of event type filters to add.
* @param {String} fetchPolicy - The fetch policy for the mutation.
*
* @returns {Promise<Array>} An array of errors.
*/
const addEventTypeFilters = async ({
$apollo,
destination,
isInstance,
eventTypeFiltersToAdd,
fetchPolicy,
}) => {
if (!eventTypeFiltersToAdd.length || !destination.id) {
return [];
}
const variables = {
destinationId: destination.id,
eventTypeFilters: eventTypeFiltersToAdd,
};
const update = (cache, { data }) => {
const { errors, eventTypeFilters } = getCreatedEventTypeFiltersData(data, isInstance);
if (errors.length || fetchPolicy === 'no-cache') {
return;
}
updateEventTypeFiltersFromCache({
store: cache,
isInstance,
destinationId: destination.id,
filters: eventTypeFilters,
});
};
const { data } = await $apollo.mutate({
mutation: isInstance
? addInstanceEventTypeFiltersToDestination
: addGroupEventTypeFiltersToDestination,
variables,
fetchPolicy,
update,
});
return getCreatedEventTypeFiltersData(data, isInstance).errors;
};
/**
* Remove event type filters from a destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {Array} eventTypeFiltersToRemove - Array of event type filters to remove.
*
* @returns {Promise<Array>} An array of errors.
*/
const removeEventTypeFilters = async ({
$apollo,
destination,
isInstance,
eventTypeFiltersToRemove,
}) => {
if (!eventTypeFiltersToRemove.length || !destination.id) {
return [];
}
const variables = {
destinationId: destination.id,
eventTypeFilters: eventTypeFiltersToRemove,
};
const { data } = await $apollo.mutate({
mutation: isInstance
? deleteInstanceEventTypeFiltersFromDestination
: deleteGroupEventTypeFiltersFromDestination,
variables,
fetchPolicy: 'no-cache',
update: () => {},
});
return isInstance
? data.auditEventsInstanceDestinationEventsDelete.errors
: data.auditEventsGroupDestinationEventsDelete.errors;
};
/**
* Add namespace filters to a destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {String} fetchPolicy - The fetch policy for the mutation.
*
* @returns {Promise<Array>} An array of errors.
*/
const addNamespaceFilters = async ({ $apollo, destination, fetchPolicy }) => {
const variables = {
destinationId: destination.id,
namespacePath: destination.namespaceFilter.namespace,
};
const update = (cache, { data }) => {
if (
data.auditEventsGroupDestinationNamespaceFilterCreate.errors.length ||
fetchPolicy === 'no-cache'
) {
return;
}
addNamespaceFilterToCache({
store: cache,
destinationId: destination.id,
filter: data.auditEventsGroupDestinationNamespaceFilterCreate.namespaceFilter,
});
};
return $apollo.mutate({
mutation: addGroupNamespaceFiltersToDestination,
variables,
fetchPolicy,
update,
});
};
/**
* Remove namespace filters from a destination.
* The API allows more than 1 namespace filter per destination,
* but we enforce 1 per destination for now.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
*
* @returns {Promise<Array>} An array of errors.
*/
const removeNamespaceFilters = async ($apollo, destination) => {
if (!destination.namespaceFilters.length) {
return [];
}
const removeFiltersPromises = destination.namespaceFilters.map(async (namespaceFilter) => {
const { data } = await $apollo.mutate({
mutation: deleteGroupNamespaceFiltersFromDestination,
variables: {
namespaceFilterId: namespaceFilter.id,
},
fetchPolicy: 'no-cache',
update: () => {},
});
return data.auditEventsGroupDestinationNamespaceFilterDelete.errors;
});
const results = await Promise.all(removeFiltersPromises);
return results.reduce((errors, errs) => [...errors, ...errs], []);
};
/**
* Executes the apollo mutaion to create a new destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {String} groupPath - The full path of the group (if not instance-level).
*
* @returns {Promise} The `$apollo.mutate` promise
*/
const executeCreateDestinationMutation = ({ $apollo, destination, isInstance, groupPath }) => {
const update = (cache, { data }) => {
if (getCreatedDestinationData(data, isInstance).errors.length) return;
addAuditEventsStreamingDestinationToCache({
store: cache,
isInstance,
fullPath: groupPath,
newDestination: getCreatedDestinationData(data, isInstance).externalAuditEventDestination,
});
};
const variables = {
input: {
name: destination.name,
secretToken: destination.secretToken,
category: getCategoryInGraphqlFormat(destination.category),
config: {
...destination.config,
},
...(isInstance ? {} : { groupPath }),
},
};
return $apollo.mutate({
mutation: isInstance ? createInstanceStreamingDestination : createGroupStreamingDestination,
variables,
update,
});
};
/**
* Create a new destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {String} groupPath - The full path of the group (if not instance-level).
* @param {Array} eventTypeFiltersToAdd - Array of event type filters to add.
* @param {Boolean} hasChangedNamespaceFilter - Flag indicating if the namespace filter has changed.
*
* @returns {Promise<{ errors: Array }>}
*/
export const createDestination = async ({
$apollo,
destination,
isInstance,
groupPath,
eventTypeFiltersToAdd,
hasChangedNamespaceFilter,
}) => {
const errors = [];
const fetchPolicy = 'network-only';
const createdDestination = {};
const { data } = await executeCreateDestinationMutation({
$apollo,
destination,
isInstance,
groupPath,
});
errors.push(...getCreatedDestinationData(data, isInstance).errors);
if (errors.length) return { errors };
createdDestination.id = getCreatedDestinationData(
data,
isInstance,
).externalAuditEventDestination.id;
errors.push(
...(await addEventTypeFilters({
$apollo,
destination: createdDestination,
isInstance,
eventTypeFiltersToAdd,
fetchPolicy,
})),
);
if (hasChangedNamespaceFilter) {
const { data: namespaceFilterData } = await addNamespaceFilters({
$apollo,
destination: {
...destination,
...createdDestination,
},
fetchPolicy,
});
errors.push(...namespaceFilterData.auditEventsGroupDestinationNamespaceFilterCreate.errors);
}
return { errors };
};
const getUpdatedDestinationData = (data, isInstance) =>
isInstance
? data.instanceAuditEventStreamingDestinationsUpdate
: data.groupAuditEventStreamingDestinationsUpdate;
/**
* Executes the apollo mutaion to update an existing destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {String} groupPath - The full path of the group (if not instance-level).
*
* @returns {Promise} The `$apollo.mutate` promise
*/
const executeUpdateDestinationMutation = ({ $apollo, destination, isInstance }) => {
const variables = {
input: {
id: destination.id,
name: destination.name,
config: {
...destination.config,
},
...(destination.secretToken ? { secretToken: destination.secretToken } : {}),
},
};
return $apollo.mutate({
mutation: isInstance ? updateInstanceStreamingDestination : updateGroupStreamingDestination,
variables,
fetchPolicy: 'no-cache',
update: () => {},
});
};
/**
* Update an existing destination.
*
* @param {Object} $apollo - The Apollo client instance.
* @param {Object} destination - The destination object.
* @param {Boolean} isInstance - Flag indicating if the destination is an instance-level destination.
* @param {Array} eventTypeFiltersToAdd - Array of event type filters to add.
* @param {Array} eventTypeFiltersToRemove - Array of event type filters to remove.
* @param {Boolean} hasChangedNamespaceFilter - Flag indicating if the namespace filter has changed.
*
* @returns {Promise<{ errors: Array }>}
*/
export const updateDestination = async ({
$apollo,
destination,
isInstance,
eventTypeFiltersToAdd,
eventTypeFiltersToRemove,
hasChangedNamespaceFilter,
}) => {
const errors = [];
const fetchPolicy = 'no-cache';
let updatedNamespaceFilter;
const { data } = await executeUpdateDestinationMutation({
$apollo,
destination,
isInstance,
});
errors.push(...getUpdatedDestinationData(data, isInstance).errors);
errors.push(
...(await removeEventTypeFilters({
$apollo,
destination,
isInstance,
eventTypeFiltersToRemove,
})),
);
errors.push(
...(await addEventTypeFilters({
$apollo,
destination,
isInstance,
eventTypeFiltersToAdd,
fetchPolicy,
})),
);
if (hasChangedNamespaceFilter) {
errors.push(...(await removeNamespaceFilters($apollo, destination)));
const { data: namespaceFilterData } = await addNamespaceFilters({
$apollo,
destination,
fetchPolicy,
});
errors.push(...namespaceFilterData.auditEventsGroupDestinationNamespaceFilterCreate.errors);
updatedNamespaceFilter =
namespaceFilterData.auditEventsGroupDestinationNamespaceFilterCreate.namespaceFilter;
}
if (errors.length) return { errors };
const updatedData = {
...getUpdatedDestinationData(data, isInstance).externalAuditEventDestination,
eventTypeFilters: JSON.parse(JSON.stringify(destination.eventTypeFilters)),
};
if (updatedNamespaceFilter) {
updatedData.namespaceFilters = [updatedNamespaceFilter];
}
updateAuditEventsStreamingDestinationFromCache({
store: $apollo.provider.defaultClient.cache,
isInstance,
updatedData,
});
return { errors };
};