/// 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; };