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

feat: Added findByIdAccess method to Proposal and Sample APIs #1529

Merged
4 changes: 2 additions & 2 deletions CI/ESS/e2e/cypress.config.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocking issue!!!
I wonder if we can import the username and passwords from functional accounts file used in tests

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default defineConfig({
lbTokenPrefix: "Bearer ",
viewportWidth: 1280,
username: "admin",
password: "am2jf70TPNZsSan",
password: "27f5fd86ae68fe740eef42b8bbd1d7d5",
secondaryUsername: "archiveManager",
secondaryPassword: "aman",
secondaryPassword: "6d3b76392e6f41b087c11f8b77e3f9de",
guestUsername: "user1",
guestUserEmail: "user1@your.site",
guestPassword: "a609316768619f154ef58db4d847b75e",
Expand Down
4 changes: 2 additions & 2 deletions CI/ESS/e2e/cypress.github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default defineConfig({
lbTokenPrefix: "Bearer ",
viewportWidth: 1280,
username: "admin",
password: "am2jf70TPNZsSan",
password: "27f5fd86ae68fe740eef42b8bbd1d7d5",
secondaryUsername: "archiveManager",
secondaryPassword: "aman",
secondaryPassword: "6d3b76392e6f41b087c11f8b77e3f9de",
guestUsername: "user1",
guestUserEmail: "user1@your.site",
guestPassword: "a609316768619f154ef58db4d847b75e",
Expand Down
4 changes: 2 additions & 2 deletions CI/ESS/e2e/cypress.nestjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default defineConfig({
lbTokenPrefix: "Bearer ",
viewportWidth: 1280,
username: "admin",
password: "am2jf70TPNZsSan",
password: "27f5fd86ae68fe740eef42b8bbd1d7d5",
secondaryUsername: "archiveManager",
secondaryPassword: "aman",
secondaryPassword: "6d3b76392e6f41b087c11f8b77e3f9de",
guestUsername: "user1",
guestUserEmail: "user1@your.site",
guestPassword: "a609316768619f154ef58db4d847b75e",
Expand Down
34 changes: 24 additions & 10 deletions CI/ESS/e2e/functionalAccounts.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,46 @@
[
{
"username": "admin",
"email": "admin@your.site",
"password": "am2jf70TPNZsSan",
"email": "admin@scicat.project",
"password": "27f5fd86ae68fe740eef42b8bbd1d7d5",
"role": "admin",
"global": true
},
{
"username": "ingestor",
"email": "scicatingestor@your.site",
"password": "aman",
"role": "ingestor",
"username": "adminIngestor",
"email": "adminingestor@scicat.project",
"password": "13f4242dc691a3ee3bb5ca2006edcdf7",
"role": "adminingestor",
"global": false
},
{
"username": "archiveManager",
"email": "scicatarchivemanager@your.site",
"password": "aman",
"email": "archivemanager@scicat.project",
"password": "6d3b76392e6f41b087c11f8b77e3f9de",
"role": "archivemanager",
"global": false
},
{
"username": "datasetIngestor",
"email": "datasetingestor@scicat.project",
"password": "bc35db76848cf9fbb7f40b6661644e97",
"role": "datasetingestor",
"global": false
},
{
"username": "proposalIngestor",
"email": "scicatproposalingestor@your.site",
"password": "aman",
"email": "proposalingestor@scicat.project",
"password": "7d8cd858fb9d0e4f5d91c34fd4016167",
"role": "proposalingestor",
"global": false
},
{
"username": "sampleIngestor",
"email": "sampleingestor@scicat.project",
"password": "e4876cb39c7dc4fe957d7c4f6a34cdd8",
"role": "sampleingestor",
"global": false
},
{
"username": "user1",
"email": "user1@your.site",
Expand Down
4 changes: 2 additions & 2 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default defineConfig({
lbTokenPrefix: "Bearer ",
viewportWidth: 1280,
username: "admin",
password: "am2jf70TPNZsSan",
password: "27f5fd86ae68fe740eef42b8bbd1d7d5",
secondaryUsername: "archiveManager",
secondaryPassword: "aman",
secondaryPassword: "6d3b76392e6f41b087c11f8b77e3f9de",
guestUsername: "user1",
guestUserEmail: "user1@your.site",
guestPassword: "a609316768619f154ef58db4d847b75e",
Expand Down
30 changes: 30 additions & 0 deletions src/app/shared/sdk/services/custom/Proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,36 @@ export class ProposalApi extends BaseLoopBackApi {
return result;
}

/**
* @method findByIdAccess
* @param {string} id ID of the resource
* @param {Function} [customHeaders] Optional custom headers function
* @return {Observable<{ canAccess: boolean }>}
*/
public findByIdAccess<T>(
id: string,
customHeaders?: Function,
): Observable<{ canAccess: boolean }> {
let _method: string = "GET";
let _url: string =
LoopBackConfig.getPath() +
"/" +
LoopBackConfig.getApiVersion() +
"/Proposals/:id/authorization";
let _routeParams: any = { id };
let _postBody: any = {};
let _urlParams: any = {};
let result = this.request(
_method,
_url,
_routeParams,
_urlParams,
_postBody,
null,
customHeaders,
);
return result;
}
/**
* Find proposal that took data at specified instrument and time
*
Expand Down
31 changes: 31 additions & 0 deletions src/app/shared/sdk/services/custom/Sample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,37 @@ export class SampleApi extends BaseLoopBackApi {
return result;
}

/**
* @method findByIdAccess
* @param {string} id ID of the resource
* @param {Function} [customHeaders] Optional custom headers function
* @return {Observable<{ canAccess: boolean }>}
*/
public findByIdAccess<T>(
id: string,
customHeaders?: Function,
): Observable<{ canAccess: boolean }> {
let _method: string = "GET";
let _url: string =
LoopBackConfig.getPath() +
"/" +
LoopBackConfig.getApiVersion() +
"/Samples/:id/authorization";
let _routeParams: any = { id };
let _postBody: any = {};
let _urlParams: any = {};
let result = this.request(
_method,
_url,
_routeParams,
_urlParams,
_postBody,
null,
customHeaders,
);
return result;
}

/**
* Get a list of sample characteristic keys
*
Expand Down
3 changes: 3 additions & 0 deletions src/app/state-management/actions/proposals.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export const fetchProposalCompleteAction = createAction(
export const fetchProposalFailedAction = createAction(
"[Proposal] Fetch Proposal Failed",
);
export const fetchProposalAccessFailedAction = createAction(
"[Proposal] Fetch Proposal Access Failed",
);

export const fetchProposalDatasetsAction = createAction(
"[Proposal] Fetch Datasets",
Expand Down
3 changes: 3 additions & 0 deletions src/app/state-management/actions/samples.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export const fetchSampleCompleteAction = createAction(
export const fetchSampleFailedAction = createAction(
"[Sample] Fetch Sample Failed",
);
export const fetchSampleAccessFailedAction = createAction(
"[Sample] Fetch Sample Access Failed",
);
export const fetchSampleAttachmentsAction = createAction(
"[Sample] Fetch Sample Attachments",
props<{ sampleId: string }>(),
Expand Down
64 changes: 53 additions & 11 deletions src/app/state-management/effects/proposals.effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Dataset,
Attachment,
} from "shared/sdk";
import { Observable } from "rxjs";
import { Observable, of, throwError } from "rxjs";
import { ProposalEffects } from "./proposals.effects";
import { TestBed } from "@angular/core/testing";
import { provideMockStore } from "@ngrx/store/testing";
Expand Down Expand Up @@ -61,6 +61,7 @@ describe("ProposalEffects", () => {
useValue: jasmine.createSpyObj("proposalApi", [
"fullquery",
"findById",
"findByIdAccess",
"createAttachments",
"updateByIdAttachments",
"destroyByIdAttachments",
Expand Down Expand Up @@ -240,29 +241,70 @@ describe("ProposalEffects", () => {

describe("fetchProposal$", () => {
const proposalId = "testId";
const permission = {
accepted: { canAccess: true },
rejected: { canAccess: false },
};

it("should result in a fetchCountCompleteAction", () => {
it("should result in a fetchProposalCompleteAction", () => {
const action = fromActions.fetchProposalAction({ proposalId });
const outcome = fromActions.fetchProposalCompleteAction({ proposal });

actions = hot("-a", { a: action });
const response = cold("-a|", { a: proposal });
proposalApi.findById.and.returnValue(response);
proposalApi.findByIdAccess
.withArgs(proposalId)
.and.returnValue(of(permission.accepted));
proposalApi.findById
.withArgs(encodeURIComponent(proposalId))
.and.returnValue(of(proposal));

actions = hot("a", { a: action });
const expected = cold("b", { b: outcome });

const expected = cold("--b", { b: outcome });
expect(effects.fetchProposal$).toBeObservable(expected);
});

it("should result in a fetchProposalFailedAction", () => {
const action = fromActions.fetchProposalAction({ proposalId });
const outcome = fromActions.fetchProposalFailedAction();
const failure = fromActions.fetchProposalFailedAction();

actions = hot("-a", { a: action });
const response = cold("-#", {});
proposalApi.findById.and.returnValue(response);
proposalApi.findByIdAccess
.withArgs(proposalId)
.and.returnValue(of(permission.accepted));
proposalApi.findById.and.returnValue(throwError(() => new Error()));

actions = hot("a", { a: action });
const expected = cold("b", { b: failure });

expect(effects.fetchProposal$).toBeObservable(expected);
});

it("should do nothing if findByIdAccess returns false", () => {
const action = fromActions.fetchProposalAction({ proposalId });

proposalApi.findByIdAccess
.withArgs(proposalId)
.and.returnValue(of(permission.rejected));

actions = hot("a", { a: action });
const expected = cold("------");

expect(effects.fetchProposal$).toBeObservable(expected);
expect(proposalApi.findById).not.toHaveBeenCalled();
});

it("should result in fetchProposalAccessFailedAction if findByIdAccess failed", () => {
const action = fromActions.fetchProposalAction({ proposalId });
const failure = fromActions.fetchProposalAccessFailedAction();

proposalApi.findByIdAccess
.withArgs(proposalId)
.and.returnValue(throwError(() => new Error()));

actions = hot("a", { a: action });
const expected = cold("b", { b: failure });

const expected = cold("--b", { b: outcome });
expect(effects.fetchProposal$).toBeObservable(expected);
expect(proposalApi.findById).not.toHaveBeenCalled();
});
});

Expand Down
26 changes: 16 additions & 10 deletions src/app/state-management/effects/proposals.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
selectFullqueryParams,
selectDatasetsQueryParams,
} from "state-management/selectors/proposals.selectors";
import { map, mergeMap, catchError, switchMap } from "rxjs/operators";
import { map, mergeMap, catchError, switchMap, filter } from "rxjs/operators";
import { of } from "rxjs";
import {
loadingAction,
Expand Down Expand Up @@ -60,16 +60,22 @@ export class ProposalEffects {
fetchProposal$ = createEffect(() => {
return this.actions$.pipe(
ofType(fromActions.fetchProposalAction),
switchMap(({ proposalId }) =>
this.proposalApi
.findById<Proposal>(encodeURIComponent(proposalId))
.pipe(
map((proposal: Proposal) =>
fromActions.fetchProposalCompleteAction({ proposal }),
),
catchError(() => of(fromActions.fetchProposalFailedAction())),
switchMap(({ proposalId }) => {
return this.proposalApi.findByIdAccess(proposalId).pipe(
filter((permission: { canAccess: boolean }) => permission.canAccess),
switchMap(() =>
this.proposalApi
.findById<Proposal>(encodeURIComponent(proposalId))
.pipe(
map((proposal: Proposal) =>
fromActions.fetchProposalCompleteAction({ proposal }),
),
catchError(() => of(fromActions.fetchProposalFailedAction())),
),
),
),
catchError(() => of(fromActions.fetchProposalAccessFailedAction())),
);
}),
);
});

Expand Down
Loading
Loading