///
import {
ApiPayload,
ComponentPayload,
ComponentState,
ComponentSyncEventStatus,
CreateCompassComponentInput,
CreateComponentInput,
CreateComponentOptions,
SdkError,
} from '@atlassian/forge-graphql-types';
import { CompassRequests } from '../../compass-requests';
import {
COMPOUND_MUTATION_NAME,
FORGE_GRAPHQL_SDK_ERROR_SOURCE,
concatGqlSegments,
mapGqlErrors,
parseCompoundMutationErrors,
prefixMutation,
transformFieldsToGql,
} from '../../helpers';
import { TIER_MISSING_VALUE } from '../../helpers/constants';
import {
updateDataManagerSegment,
updateLabelsSegment,
updateLinksSegment,
updateRelationshipsSegment,
} from '../../helpers/gql-segments';
import { transformCustomFieldsToGql } from '../../helpers/transformFields';
declare module '../../compass-requests' {
interface CompassRequests {
/**
* Creates a new component.
*
* **Required Oauth Scopes:** `write:component:compass`
*/
createComponent(
input: CreateComponentInput,
): Promise>;
}
}
async function createBaseComponent(
api: CompassRequests,
input: CreateComponentInput,
) {
const {
cloudId,
name,
description,
slug,
type,
typeId,
fields,
customFields,
ownerId,
options,
state = ComponentState.PENDING,
} = input;
let baseComponentInput: CreateCompassComponentInput & {
options?: CreateComponentOptions;
};
try {
baseComponentInput = {
name,
description,
slug,
type,
typeId,
ownerId,
options,
state,
};
const transformedFields = transformFieldsToGql(fields);
if (transformedFields.length > 0) {
baseComponentInput.fields = transformedFields;
}
if (customFields && customFields.length) {
baseComponentInput.customFields =
transformCustomFieldsToGql(customFields);
}
} catch (e) {
if (e.message === TIER_MISSING_VALUE) {
return { errors: e.message, componentId: null as string };
}
throw e;
}
const { data, errors } = await api.createBaseComponent(
cloudId,
baseComponentInput,
);
let id;
try {
({
component: { id },
} = data);
} catch {
console.error(
`Error: Could not get component id in createComponent response`,
);
}
return {
errors,
componentId: errors.length === 0 ? id : null,
};
}
async function createComponentWithExternalAlias(
api: CompassRequests,
input: CreateComponentInput,
) {
const { externalAlias } = input;
let errorsAcc = [] as Array;
/* eslint-disable prefer-const */
let componentId;
let errors = [];
({ errors, componentId } = await createBaseComponent(api, input));
/* eslint-enable prefer-const */
if (errors.length === 0) {
try {
const { errors: createExternalAliasErrors } =
await api.createExternalAlias({
componentId,
externalAlias,
});
errorsAcc = errorsAcc.concat(createExternalAliasErrors);
if (createExternalAliasErrors && createExternalAliasErrors.length > 0) {
const { errors: deleteComponentErrors } = await api.deleteComponent({
id: componentId,
});
errorsAcc = errorsAcc.concat(deleteComponentErrors);
}
} catch (e) {
errorsAcc.push({
message: e.message,
errorSource: FORGE_GRAPHQL_SDK_ERROR_SOURCE,
errorType: e.constructor.name,
});
const { errors: deleteComponentErrors } = await api.deleteComponent({
id: componentId,
});
errorsAcc = errorsAcc.concat(deleteComponentErrors);
}
} else {
errorsAcc = errorsAcc.concat(errors);
}
return {
errors: errorsAcc,
componentId,
};
}
CompassRequests.prototype.createComponent = async function (
this: CompassRequests,
input: CreateComponentInput,
) {
const { links, relationships, labels, dataManager, externalAlias } = input;
let errorsAcc = [] as Array;
let errors = [];
let componentId;
let createdComponent = {};
if (externalAlias) {
({ componentId, errors } = await createComponentWithExternalAlias(
this,
input,
));
errorsAcc = errorsAcc.concat(errors);
} else {
({ componentId, errors } = await createBaseComponent(this, input));
errorsAcc = errorsAcc.concat(errors);
}
if (componentId) {
// Generate update component round trip request
const gqlSegments = [];
if (dataManager) {
gqlSegments.push(
updateDataManagerSegment(componentId, null, dataManager),
);
}
gqlSegments.push(updateLinksSegment(componentId, [], links || []));
gqlSegments.push(
updateRelationshipsSegment(componentId, [], relationships),
);
gqlSegments.push(updateLabelsSegment(componentId, [], labels || []));
const { mutation, variables, parameters } = concatGqlSegments(gqlSegments);
if (mutation.trim() !== '') {
const resp = await this.api.requestGraph(
prefixMutation(mutation, COMPOUND_MUTATION_NAME, parameters),
variables,
'createComponent',
);
const { errors: createComponentErrors, data } = await resp.json();
errorsAcc = errorsAcc.concat(
parseCompoundMutationErrors(data, variables),
);
errorsAcc = errorsAcc.concat(mapGqlErrors(createComponentErrors));
}
if (dataManager) {
// TODO: Make ServerError distinct from UserError
const { errors: updateDataManagerErrors } = await this.updateDataManager({
componentId,
externalSourceURL: dataManager.externalSourceURL,
lastSyncEvent: {
status:
errorsAcc.length > 0
? ComponentSyncEventStatus.UserError
: ComponentSyncEventStatus.Success,
lastSyncErrors: errorsAcc.map((error) => error.message),
},
});
errorsAcc = errorsAcc.concat(updateDataManagerErrors);
}
/* eslint-disable prefer-const */
let getComponentErrors;
({
errors: getComponentErrors,
data: { component: createdComponent },
} = await this.getComponent({
componentId,
}));
errorsAcc = errorsAcc.concat(getComponentErrors);
/* eslint-enable prefer-const */
}
return {
errors: errorsAcc,
data: { component: createdComponent || {} },
success: errorsAcc.length === 0,
} as ApiPayload;
};