///
import {
ApiPayload,
ComponentPayload,
ComponentSyncEventStatus,
SdkError,
UpdateComponentInput,
UpdateComponentOptions,
} from '@atlassian/forge-graphql-types';
import {
updateBaseComponentSegment,
updateComponentTypeSegment,
updateDataManagerSegment,
updateLabelsSegment,
updateLinksSegment,
updateRelationshipsSegment,
} from '../../helpers/gql-segments';
import { CompassRequests } from '../../compass-requests';
import {
COMPOUND_MUTATION_NAME,
concatGqlSegments,
hasChanged,
isEmpty,
mapGqlErrors,
parseCompoundMutationErrors,
prefixMutation,
} from '../../helpers';
import {
fgqlActionSubjects,
fgqlActions,
fgqlSources,
} from '../../helpers/analyticTypes';
declare module '../../compass-requests' {
interface CompassRequests {
/**
* Updates an existing component.
*
* **Required Oauth Scopes:** `write:component:compass`, `write:event:compass`
*/
updateComponent(
input: UpdateComponentInput & { options?: UpdateComponentOptions },
): Promise>;
}
}
CompassRequests.prototype.updateComponent = async function (
this: CompassRequests,
input: UpdateComponentInput & { options?: UpdateComponentOptions },
) {
let errorsAcc = [] as Array;
let { currentComponent } = input;
const { id, dataManager, links, relationships, labels, typeId } = input;
if (!currentComponent) {
const {
errors: updatedComponentErrors,
success,
data,
} = await this.getComponent({
componentId: id,
options: {
includeLinks: true,
},
});
errorsAcc = errorsAcc.concat(updatedComponentErrors);
if (errorsAcc.length > 0) {
return {
errors: errorsAcc,
success,
data: { component: {} },
} as ApiPayload;
}
currentComponent = data.component;
}
// Generate update component round trip request
const gqlSegments = [];
gqlSegments.push(
updateDataManagerSegment(id, currentComponent.dataManager, dataManager),
);
if (typeId) {
gqlSegments.push(updateComponentTypeSegment({ componentId: id, typeId }));
}
gqlSegments.push(updateBaseComponentSegment(input));
gqlSegments.push(updateLinksSegment(id, currentComponent.links, links));
gqlSegments.push(
updateRelationshipsSegment(
id,
currentComponent.relationships,
relationships,
),
);
gqlSegments.push(updateLabelsSegment(id, currentComponent.labels, labels));
const analyticHeaders: Record = {};
if (input.options?.updatedFromFile) {
const updatedFromFileMetadata = {
action: fgqlActions.updated,
actionSubject: fgqlActionSubjects.component,
actionSubjectId: id,
source: fgqlSources.configAsCode,
} as Record;
// Track fields that updateComponent updates: dataManager, links, relationships, labels, typeId
if (hasChanged(dataManager, currentComponent.dataManager)) {
updatedFromFileMetadata.dataManager = 'true';
}
if (hasChanged(links, currentComponent.links)) {
updatedFromFileMetadata.links = 'true';
}
if (hasChanged(relationships, currentComponent.relationships)) {
updatedFromFileMetadata.relationships = 'true';
}
if (hasChanged(labels, currentComponent.labels)) {
updatedFromFileMetadata.labels = 'true';
}
if (hasChanged(typeId, currentComponent.typeId)) {
updatedFromFileMetadata.typeId = 'true';
}
analyticHeaders.updatedFromFile = JSON.stringify(updatedFromFileMetadata);
}
const { mutation, variables, parameters } = concatGqlSegments(gqlSegments);
const resp = await this.api.requestGraph(
prefixMutation(mutation, COMPOUND_MUTATION_NAME, parameters),
variables,
'updateComponent',
analyticHeaders,
);
const { errors, data } = await resp.json();
errorsAcc = errorsAcc.concat(parseCompoundMutationErrors(data, variables));
errorsAcc = errorsAcc.concat(mapGqlErrors(errors));
if (
dataManager !== undefined ||
(currentComponent.dataManager && !isEmpty(dataManager))
) {
// TODO: Make ServerError distinct from UserError
const { errors: updateDataManageErrors } = await this.updateDataManager({
componentId: id,
externalSourceURL:
(dataManager && dataManager.externalSourceURL) ||
(currentComponent.dataManager &&
currentComponent.dataManager.externalSourceURL),
lastSyncEvent: {
status:
errorsAcc.length > 0
? ComponentSyncEventStatus.UserError
: ComponentSyncEventStatus.Success,
lastSyncErrors: errorsAcc.map((error) => error.message),
},
});
const errorMessages = updateDataManageErrors
.map((updateDataManageError) => updateDataManageError.message)
.join(', ');
console.error(`Error while updating dataManager: ${errorMessages}`);
errorsAcc = errorsAcc.concat(updateDataManageErrors);
}
const {
errors: updatedComponentErrors,
data: { component: updatedComponent },
} = await this.getComponent({
componentId: id,
});
errorsAcc = errorsAcc.concat(updatedComponentErrors);
return {
errors: errorsAcc,
success: errorsAcc.length === 0,
data: { component: updatedComponent || {} },
} as ApiPayload;
};