Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIIN-2784: Set central tenant id in the request when Member tenant deletes a shared record #2416

Merged
merged 11 commits into from
Mar 25, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* Staff suppress facet - use `No` as default value. Hide the facet when users don't have permissions to use it. Refs UIIN-2596.
* Add a new search option for instances called `LCCN normalization`. Refs UIIN-2245.
* Add and adjust collapse/expand buttons for consortial instances. Refs UIIN-2711.
* Set central tenant id in the request when Member tenant deletes a shared record. Fixes UIIN-2784.

## [10.0.10](https://github.com/folio-org/ui-inventory/tree/v10.0.10) (2024-01-17)
[Full Changelog](https://github.com/folio-org/ui-inventory/compare/v10.0.9...v10.0.10)
Expand Down
20 changes: 7 additions & 13 deletions src/ViewInstance.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
isInstanceShadowCopy,
isMARCSource,
getLinkedAuthorityIds,
setRecordForDeletion,
} from './utils';
import {
CONSORTIUM_PREFIX,
Expand Down Expand Up @@ -169,14 +170,6 @@ class ViewInstance extends React.Component {
accumulate: true,
throwErrors: false,
},
setForDeletion: {
type: 'okapi',
throwErrors: false,
accumulate: true,
DELETE: {
path: 'inventory/instances/:{id}/mark-deleted',
},
},
authorities: {
type: 'okapi',
path: 'authority-storage/authorities',
Expand Down Expand Up @@ -587,15 +580,19 @@ class ViewInstance extends React.Component {

handleSetForDeletion = () => {
const {
mutator,
okapi,
isShared,
centralTenantId,
refetchInstance,
selectedInstance: {
title: instanceTitle,
id,
source,
},
} = this.props;
const tenantId = isShared && !isInstanceShadowCopy(source) ? centralTenantId : okapi.tenant;

mutator.setForDeletion.DELETE(id)
setRecordForDeletion(okapi, id, tenantId)
.then(async () => {
this.handleCloseSetForDeletionModal();

Expand Down Expand Up @@ -1207,9 +1204,6 @@ ViewInstance.propTypes = {
POST: PropTypes.func.isRequired,
GET: PropTypes.func.isRequired,
}).isRequired,
setForDeletion: PropTypes.shape({
DELETE: PropTypes.func.isRequired,
}).isRequired,
authorities: PropTypes.shape({
GET: PropTypes.func.isRequired,
}).isRequired,
Expand Down
15 changes: 12 additions & 3 deletions src/ViewInstance.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const spyOncollapseAllSections = jest.spyOn(require('@folio/stripes/components')
const spyOnexpandAllSections = jest.spyOn(require('@folio/stripes/components'), 'expandAllSections');

const spyOnGetUserTenantsPermissions = jest.spyOn(utils, 'getUserTenantsPermissions');
const spyOnSetRecordForDeletion = jest.spyOn(utils, 'setRecordForDeletion');

const location = {
pathname: '/testPathName',
Expand Down Expand Up @@ -1117,20 +1118,28 @@ describe('ViewInstance', () => {
fireEvent.click(screen.getByText('Set record for deletion'));
fireEvent.click(screen.getByText('Confirm'));

expect(defaultProp.mutator.setForDeletion.DELETE).toHaveBeenCalledWith(defaultProp.selectedInstance.id);
expect(spyOnSetRecordForDeletion).toHaveBeenCalledWith(
{ tenant: defaultProp.okapi.tenant },
defaultProp.selectedInstance.id,
defaultProp.okapi.tenant,
);
expect(screen.queryByText(`${defaultProp.selectedInstance.title} has been set for deletion`)).toBeDefined();
});

describe('when there\'s an error', () => {
it('should throw an error with id of the instance and show error message', async () => {
defaultProp.mutator.setForDeletion.DELETE.mockRejectedValueOnce();
global.fetch.mockRejectedValueOnce({ ok: false });

renderViewInstance();

fireEvent.click(screen.getByText('Set record for deletion'));
fireEvent.click(screen.getByText('Confirm'));

expect(defaultProp.mutator.setForDeletion.DELETE).toHaveBeenCalledWith(defaultProp.selectedInstance.id);
expect(spyOnSetRecordForDeletion).toHaveBeenCalledWith(
{ tenant: defaultProp.okapi.tenant },
defaultProp.selectedInstance.id,
defaultProp.okapi.tenant,
);
expect(screen.queryByText(`${defaultProp.selectedInstance.title} was not set for deletion`)).toBeDefined();
});
});
Expand Down
30 changes: 30 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -886,3 +886,33 @@ export const clearStorage = () => {
removeItem(`${namespace}.${segment}.lastOpenRecord`);
});
};

export const setRecordForDeletion = async (okapi, id, tenantId) => {
const {
url,
token,
} = okapi;

try {
const path = `${url}/inventory/instances/${id}/mark-deleted`;
const response = await fetch(path, {
method: 'DELETE',
headers: {
[OKAPI_TENANT_HEADER]: tenantId,
[CONTENT_TYPE_HEADER]: 'application/json',
...(token && { [OKAPI_TOKEN_HEADER]: token }),
},
credentials: 'include',
});

if (!response.ok) {
throw new Error('Cannot set record for deletion');
}

return response;
} catch (error) {
console.error(error); // eslint-disable-line no-console

return error;
}
};
71 changes: 70 additions & 1 deletion src/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ import {
validateAlphaNumericField,
getQueryTemplate,
switchAffiliation,
setRecordForDeletion,
} from './utils';
import { browseModeOptions } from './constants';
import {
CONTENT_TYPE_HEADER,
OKAPI_TENANT_HEADER,
OKAPI_TOKEN_HEADER,
browseModeOptions,
} from './constants';

describe('validateRequiredField', () => {
const expectedResult = <FormattedMessage id="ui-inventory.hridHandling.validation.enterValue" />;
Expand Down Expand Up @@ -179,3 +185,66 @@ describe('switchAffiliation', () => {
});
});
});

describe('setRecordForDeletion', () => {
afterEach(() => {
global.fetch.mockClear();
});

afterAll(() => {
delete global.fetch;
});

const instanceId = 'testInstanceId';
const tenantId = 'testTenantId';
const okapi = {
token: 'token',
url: 'url/test',
};

describe('when the request was fulfilled successfuly', () => {
it('should return the appropriate response', () => {
global.fetch = jest.fn().mockReturnValue({ ok: true });

setRecordForDeletion(okapi, instanceId, tenantId);

expect(global.fetch).toHaveBeenCalledWith(
`${okapi.url}/inventory/instances/${instanceId}/mark-deleted`,
{
credentials: 'include',
headers: expect.objectContaining({
[OKAPI_TENANT_HEADER]: tenantId,
[OKAPI_TOKEN_HEADER]: okapi.token,
[CONTENT_TYPE_HEADER]: 'application/json',
}),
method: 'DELETE',
},
);

expect(global.fetch.mock.results[0].value.ok).toBe(true);
});
});

describe('when the request was fulfilled with an error', () => {
it('should the appropriate response', () => {
global.fetch = jest.fn().mockReturnValue({ ok: false });

setRecordForDeletion(okapi, instanceId, tenantId);

expect(global.fetch).toHaveBeenCalledWith(
`${okapi.url}/inventory/instances/${instanceId}/mark-deleted`,
{
credentials: 'include',
headers: expect.objectContaining({
[OKAPI_TENANT_HEADER]: tenantId,
[OKAPI_TOKEN_HEADER]: okapi.token,
[CONTENT_TYPE_HEADER]: 'application/json',
}),
method: 'DELETE',
},
);

expect(global.fetch.mock.results[0].value.ok).toBe(false);
});
});
});
Loading