Skip to content

Commit

Permalink
Merge pull request #269 from Smithsonian/feature/440-Edit-Metadata
Browse files Browse the repository at this point in the history
Feature 440: finalize MVP handling of subject edit and publishing to Edan
  • Loading branch information
shintung00 authored Dec 9, 2021
2 parents ad40d98 + 6288712 commit 60969ad
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { clearLicenseAssignment, assignLicense, publish } from '../../hooks/useD
import { getTermForSystemObjectType } from '../../../../utils/repository';
import { LoadingButton } from '../../../../components';
import { toast } from 'react-toastify';
import { ePublishedState } from '../../../../types/server';
import { eSystemObjectType, ePublishedState } from '../../../../types/server';

const useStyles = makeStyles(({ palette, typography }) => ({
detail: {
Expand Down Expand Up @@ -89,11 +89,12 @@ interface ObjectDetailsProps {
publishable: boolean;
retired: boolean;
hideRetired?: boolean;
hidePublishState?: boolean;
objectType?: number;
originalFields?: GetSystemObjectDetailsResult;
onRetiredUpdate?: (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => void;
onLicenseUpdate?: (event) => void;
path?: RepositoryPath[][] | null;
updateData?: () => Promise<boolean>;
idSystemObject: number;
license?: number;
licenseInheritance?: number | null;
Expand All @@ -110,15 +111,16 @@ function ObjectDetails(props: ObjectDetailsProps): React.ReactElement {
publishable,
retired,
hideRetired,
hidePublishState,
objectType,
disabled,
originalFields,
onRetiredUpdate,
onLicenseUpdate,
idSystemObject,
license,
licenseInheritance,
path
path,
updateData
} = props;
const [licenseList, setLicenseList] = useState<License[]>([]);
const [loading, setLoading] = useState(false);
Expand Down Expand Up @@ -179,10 +181,20 @@ function ObjectDetails(props: ObjectDetailsProps): React.ReactElement {
const onPublish = async () => { onPublishWorker(ePublishedState.ePublished, 'Publish'); };
const onAPIOnly = async () => { onPublishWorker(ePublishedState.eAPIOnly, 'Publish for API Only'); };
const onUnpublish = async () => { onPublishWorker(ePublishedState.eNotPublished, 'Unpublish'); };
const onSyncToEdan = async () => { onPublishWorker(ePublishedState.ePublished, 'Sync to Edan'); };

const onPublishWorker = async (eState: number, action: string) => {
setLoading(true);

// if we're attempting to publish a subject, call the passed in update method first to persist metadata edits
if (objectType === eSystemObjectType.eSubject && updateData !== undefined) {
if (!await updateData()) {
toast.error(`${action} failed while updating object`);
setLoading(false);
return;
}
}

const { data } = await publish(idSystemObject, eState);
if (data?.publish?.success)
toast.success(`${action} succeeded`);
Expand All @@ -198,7 +210,7 @@ function ObjectDetails(props: ObjectDetailsProps): React.ReactElement {
<Detail idSystemObject={project?.idSystemObject} label='Project' value={project?.name} />
<Detail idSystemObject={subject?.idSystemObject} label='Subject' value={subject?.name} />
<Detail idSystemObject={item?.idSystemObject} label='Item' value={item?.name} />
{!hidePublishState && (
{(objectType === eSystemObjectType.eScene) && (
<Detail
label='Publish State'
valueComponent={
Expand All @@ -211,6 +223,17 @@ function ObjectDetails(props: ObjectDetailsProps): React.ReactElement {
}
/>
)}
{(objectType === eSystemObjectType.eSubject) && (
<Detail
label='Edan Sync State'
valueComponent={
<Box className={classes.inheritedLicense}>
<Typography>{publishedState}</Typography>
&nbsp;<LoadingButton onClick={onSyncToEdan} className={classes.loadingBtn} loading={loading} disabled={!publishable}>Sync to Edan</LoadingButton>
</Box>
}
/>
)}
{!hideRetired && (
<Detail
label='Retired'
Expand Down
18 changes: 10 additions & 8 deletions client/src/pages/Repository/components/DetailsView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -322,14 +322,14 @@ function DetailsView(): React.ReactElement {
setUpdatedData(updatedDataFields);
};

const updateData = async (): Promise<void> => {
const updateData = async (): Promise<boolean> => {
toast.dismiss();
setIsUpdatingData(true);
const identifierCheck = checkIdentifiersBeforeUpdate();
if (identifierCheck.length) {
identifierCheck.forEach(error => toast.error(error));
setIsUpdatingData(false);
return;
return false;
}

const stateIdentifiersWithIdSystemObject: UpdateIdentifier[] = stateIdentifiers.map(({ id, identifier, identifierType, idIdentifier, preferred }) => {
Expand All @@ -350,15 +350,15 @@ function DetailsView(): React.ReactElement {
const invalidMetadata = validateMetadataFields();
if (invalidMetadata.length) {
invalidMetadata.forEach(message => toast.error(message, { autoClose: false }));
return;
return false;
}

// Create another validation here to make sure that the appropriate SO types are being checked
const errors = await getDetailsViewFieldErrors(updatedData, objectType);
if (errors.length) {
errors.forEach(error => toast.error(`${error}`, { autoClose: false }));
setIsUpdatingData(false);
return;
return false;
}

try {
Expand Down Expand Up @@ -455,18 +455,19 @@ function DetailsView(): React.ReactElement {
}

const metadata = getAllMetadataEntries().filter(entry => entry.Name);
console.log('metadata', metadata);
// console.log('metadata', metadata);
updatedData.Metadata = metadata;

const { data } = await updateDetailsTabData(idSystemObject, idObject, objectType, updatedData);
if (data?.updateObjectDetails?.success) {
toast.success('Data saved successfully');
} else {
return true;
} else
throw new Error(data?.updateObjectDetails?.message ?? '');
}
} catch (error) {
if (error instanceof Error)
toast.error(error.toString() || 'Failed to save updated data');
return false;
} finally {
setIsUpdatingData(false);
}
Expand Down Expand Up @@ -494,14 +495,15 @@ function DetailsView(): React.ReactElement {
publishedEnum={publishedEnum}
publishable={publishable}
retired={withDefaultValueBoolean(details.retired, false)}
hidePublishState={objectType !== eSystemObjectType.eScene}
objectType={objectType}
onRetiredUpdate={onRetiredUpdate}
onLicenseUpdate={onLicenseUpdate}
originalFields={data.getSystemObjectDetails}
license={withDefaultValueNumber(details.idLicense, 0)}
idSystemObject={idSystemObject}
licenseInheritance={licenseInheritance}
path={objectAncestors}
updateData={updateData}
/>
<Box display='flex' flex={2.2} flexDirection='column'>
<IdentifierList
Expand Down
4 changes: 3 additions & 1 deletion client/src/types/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,7 @@ export type PublishInput = {
export type PublishResult = {
__typename?: 'PublishResult';
success: Scalars['Boolean'];
eState?: Maybe<Scalars['Int']>;
message?: Maybe<Scalars['String']>;
};

Expand Down Expand Up @@ -2879,7 +2880,7 @@ export type PublishMutation = (
{ __typename?: 'Mutation' }
& { publish: (
{ __typename?: 'PublishResult' }
& Pick<PublishResult, 'success' | 'message'>
& Pick<PublishResult, 'success' | 'eState' | 'message'>
) }
);

Expand Down Expand Up @@ -4602,6 +4603,7 @@ export const PublishDocument = gql`
mutation publish($input: PublishInput!) {
publish(input: $input) {
success
eState
message
}
}
Expand Down
46 changes: 41 additions & 5 deletions server/collections/impl/PublishSubject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ export class PublishSubject {
return res;

this.edanRecord = await ICol.createEdanMDM(this.edanMDM, 0, true);
if (this.edanRecord)
LOG.info(`PublishSubject.publish ${this.edanRecord.url} succeeded with Edan status ${this.edanRecord.status}, publicSearch ${this.edanRecord.publicSearch}`, LOG.LS.eCOLL); // :\nEdanMDM=${JSON.stringify(this.edanMDM, H.Helpers.saferStringify)}\nEdan Record=${JSON.stringify(this.edanRecord, H.Helpers.saferStringify)}`, LOG.LS.eCOLL); // `, LOG.LS.eCOLL); //
else
if (!this.edanRecord) {
LOG.error(`PublishSubject.publish ${JSON.stringify(this.edanMDM, H.Helpers.saferStringify)} failed`, LOG.LS.eCOLL);
return PublishSubject.returnResults(false, 'Edan publishing failed');
}

return PublishSubject.returnResults(this.edanRecord !== null, this.edanRecord !== null ? undefined : 'Edan publishing failed');
// update SystemObjectVersion.PublishedState
res = await this.updatePublishedState(DBAPI.ePublishedState.ePublished);
if (!res.success)
return res;
return PublishSubject.returnResults(true);
}

private async analyze(): Promise<H.IOResults> {
Expand Down Expand Up @@ -91,7 +95,7 @@ export class PublishSubject {
case 'record id': this.edanMDM.descriptiveNonRepeating.record_ID = values[0]; nonRepeating = true; break;
case 'unit': await this.handleUnit(values[0]); nonRepeating = true; break;
case 'license': await this.handleLicense(values[0]); nonRepeating = true; break;
case 'license text': this.edanMDM.descriptiveNonRepeating.metadata_usage!.text = values[0]; nonRepeating = true; break; // eslint-disable-line @typescript-eslint/no-non-null-assertion
case 'license text': this.edanMDM.descriptiveNonRepeating.metadata_usage!.content = values[0]; nonRepeating = true; break; // eslint-disable-line @typescript-eslint/no-non-null-assertion

case 'object type': this.edanMDM.indexedStructured!.object_type = values; break; // eslint-disable-line @typescript-eslint/no-non-null-assertion
case 'date': this.edanMDM.indexedStructured!.date = values; break; // eslint-disable-line @typescript-eslint/no-non-null-assertion
Expand Down Expand Up @@ -163,4 +167,36 @@ export class PublishSubject {
}
return retValue;
}

private async updatePublishedState(ePublishedStateIntended: DBAPI.ePublishedState): Promise<H.IOResults> {
let systemObjectVersion: DBAPI.SystemObjectVersion | null = await DBAPI.SystemObjectVersion.fetchLatestFromSystemObject(this.idSystemObject);
if (systemObjectVersion) {
if (systemObjectVersion.publishedStateEnum() !== ePublishedStateIntended) {
systemObjectVersion.setPublishedState(ePublishedStateIntended);
if (!await systemObjectVersion.update()) {
const error: string = `PublishSubject.updatePublishedState unable to update published state for ${JSON.stringify(systemObjectVersion, H.Helpers.saferStringify)}`;
LOG.error(error, LOG.LS.eCOLL);
return { success: false, error };
}
}

return { success: true };
}

systemObjectVersion = new DBAPI.SystemObjectVersion({
idSystemObject: this.idSystemObject,
PublishedState: ePublishedStateIntended,
DateCreated: new Date(),
Comment: 'Created by Packrat',
idSystemObjectVersion: 0
});

if (!await systemObjectVersion.create()) {
const error: string = `PublishSubject.updatePublishedState could not create SystemObjectVersion for idSystemObject ${this.idSystemObject}`;
LOG.error(error, LOG.LS.eCOLL);
return { success: false, error };
}

return { success: true };
}
}
1 change: 1 addition & 0 deletions server/graphql/api/mutations/systemobject/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const publish = gql`
mutation publish($input: PublishInput!) {
publish(input: $input) {
success
eState
message
}
}
Expand Down
1 change: 1 addition & 0 deletions server/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1276,6 +1276,7 @@ input PublishInput {

type PublishResult {
success: Boolean!
eState: Int
message: String
}

Expand Down
1 change: 1 addition & 0 deletions server/graphql/schema/systemobject/mutations.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -258,5 +258,6 @@ input PublishInput {

type PublishResult {
success: Boolean!
eState: Int
message: String
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CreateSubjectWithIdentifiersResult, MutationCreateSubjectWithIdentifiersArgs } from '../../../../../types/graphql';
import { Parent, Context } from '../../../../../types/resolvers';
import { handleMetadata, publishSubject } from './updateObjectDetails';
import { handleMetadata } from './updateObjectDetails';
import * as DBAPI from '../../../../../db';
import * as COL from '../../../../../collections/interface';
import * as LOG from '../../../../../utils/logger';
Expand Down Expand Up @@ -98,4 +98,10 @@ function sendResult(success: boolean, message?: string): CreateSubjectWithIdenti
if (!success)
LOG.error(`createSubjectWithIdentifier: ${message}`, LOG.LS.eGQL);
return { success, message: message ?? '' };
}
}

async function publishSubject(idSystemObject: number): Promise<H.IOResults> {
const ICol: COL.ICollection = COL.CollectionFactory.getInstance();
const success: boolean = await ICol.publish(idSystemObject, DBAPI.ePublishedState.ePublished);
return { success, error: success ? '' : 'Error encountered during publishing' };
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ export default async function publish(_: Parent, args: MutationPublishArgs): Pro

const ICol: COL.ICollection = COL.CollectionFactory.getInstance();
const success: boolean = await ICol.publish(idSystemObject, eState);
return { success, message: success ? '' : 'Error encountered during publishing' };
if (success)
return { success, eState };
return { success, message: 'Error encountered during publishing' };
}
Original file line number Diff line number Diff line change
Expand Up @@ -444,12 +444,6 @@ export default async function updateObjectDetails(_: Parent, args: MutationUpdat
break;
}

if (objectType === eSystemObjectType.eSubject) {
const publishRes: H.IOResults = await publishSubject(idSystemObject);
if (!publishRes.success)
return sendResult(false, publishRes.error);
}

return { success: true, message: '' };
}

Expand Down Expand Up @@ -503,9 +497,3 @@ export async function handleMetadata(idSystemObject: number, metadatas: Metadata
}
return { success: true };
}

export async function publishSubject(idSystemObject: number): Promise<H.IOResults> {
const ICol: COL.ICollection = COL.CollectionFactory.getInstance();
const success: boolean = await ICol.publish(idSystemObject, DBAPI.ePublishedState.ePublished);
return { success, error: success ? '' : 'Error encountered during publishing' };
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,26 @@ async function getPublishedState(idSystemObject: number, oID: DBAPI.ObjectIDAndT
const publishedEnum: DBAPI.ePublishedState = systemObjectVersion ? systemObjectVersion.publishedStateEnum() : DBAPI.ePublishedState.eNotPublished;
const publishedState: string = DBAPI.PublishedStateEnumToString(publishedEnum);

const mayBePublished: boolean = (LR != null) &&
(LR.License != null) &&
(DBAPI.LicenseRestrictLevelToPublishedStateEnum(LR.License.RestrictLevel) !== DBAPI.ePublishedState.eNotPublished);

let publishable: boolean = false;
if (oID && oID.eObjectType == DBAPI.eSystemObjectType.eScene) {
const scene: DBAPI.Scene | null = await DBAPI.Scene.fetch(oID.idObject);
if (scene)
publishable = scene.ApprovedForPublication && // Approved for Publication
scene.PosedAndQCd && // Posed and QCd
mayBePublished; // License defined and allows publishing
else
LOG.error(`Unable to compute scene for ${JSON.stringify(oID)}`, LOG.LS.eGQL);
if (oID) {
switch (oID.eObjectType) {
case DBAPI.eSystemObjectType.eScene: {
const scene: DBAPI.Scene | null = await DBAPI.Scene.fetch(oID.idObject);
if (scene) {
const mayBePublished: boolean = (LR != null) &&
(LR.License != null) &&
(DBAPI.LicenseRestrictLevelToPublishedStateEnum(LR.License.RestrictLevel) !== DBAPI.ePublishedState.eNotPublished);
publishable = scene.ApprovedForPublication && // Approved for Publication
scene.PosedAndQCd && // Posed and QCd
mayBePublished; // License defined and allows publishing
} else
LOG.error(`Unable to compute scene for ${JSON.stringify(oID)}`, LOG.LS.eGQL);
} break;

case DBAPI.eSystemObjectType.eSubject:
publishable = true;
break;
}
}
return { publishedState, publishedEnum, publishable };
}
Expand Down
1 change: 1 addition & 0 deletions server/types/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,7 @@ export type PublishInput = {
export type PublishResult = {
__typename?: 'PublishResult';
success: Scalars['Boolean'];
eState?: Maybe<Scalars['Int']>;
message?: Maybe<Scalars['String']>;
};

Expand Down

0 comments on commit 60969ad

Please sign in to comment.