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

[DCJ-260][risk=no] Converted User.js -> User.ts #2557

Merged
merged 12 commits into from
Jul 1, 2024
Empty file added src/libs/ajax/User.test.ts
Empty file.
90 changes: 66 additions & 24 deletions src/libs/ajax/User.js → src/libs/ajax/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,41 @@ import { cloneDeep, flow, unset } from 'lodash/fp';
import { Config } from '../config';
import axios from 'axios';
import { getApiUrl, fetchOk, fetchAny } from '../ajax';

import { DuosUserResponse, UpdateDuosUserResponse } from 'src/types/responseTypes';
import { CreateDuosUserRequest, UpdateDuosUserRequestV1, UpdateDuosUserRequestV2 } from 'src/types/requestTypes';
import { AcknowledgementMap, ApprovedDataset, DataSet, DuosUser, SimplifiedDuosUser } from 'src/types/model';

export const User = {
getMe: async () => {
getMe: async (): Promise<DuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/me`;
const res = await axios.get(url, Config.authOpts());
return res.data;
},

getById: async (id) => {
getById: async (id: number): Promise<DuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/${id}`;
const res = await axios.get(url, Config.authOpts());
return res.data;
},

list: async (roleName) => {
//TODO: Determine what are legal roleNames (swagger says only these two)
list: async (roleName: 'Admin' | 'SigningOfficial'): Promise<DuosUserResponse[]> => {
const url = `${await getApiUrl()}/api/user/role/${roleName}`;
const res = await fetchOk(url, Config.authOpts());
return res.json();
},

create: async (user) => {
create: async (request: CreateDuosUserRequest) => {
const url = `${await getApiUrl()}/api/dacuser`;
try {
const res = await fetchOk(url, fp.mergeAll([Config.authOpts(), Config.jsonBody(user), { method: 'POST' }]));
const res = await fetchOk(
url,
fp.mergeAll([
Config.authOpts(),
Config.jsonBody(request),
{ method: 'POST' },
])
);
if (res.ok) {
return res.json;
}
Expand All @@ -36,11 +46,18 @@ export const User = {
}
},

updateSelf: async (payload) => {
updateSelf: async (payload: UpdateDuosUserRequestV1): Promise<UpdateDuosUserResponse> => {
const url = `${await getApiUrl()}/api/user`;
// We should not be updating the user's create date, associated institution, or library cards
try {
const res = await fetchOk(url, fp.mergeAll([Config.authOpts(), Config.jsonBody(payload), { method: 'PUT' }]));
const res = await fetchOk(
url,
fp.mergeAll([
Config.authOpts(),
Config.jsonBody(payload),
{ method: 'PUT' },
])
);
if (res.ok) {
return res.json();
}
Expand All @@ -49,7 +66,7 @@ export const User = {
}
},

update: async (user, userId) => {
update: async (user: UpdateDuosUserRequestV2, userId: number): Promise<UpdateDuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/${userId}`;
// We should not be updating the user's create date, associated institution, or library cards
let filteredUser = flow(
Expand All @@ -59,7 +76,14 @@ export const User = {
unset('updatedUser.libraryCards')
)(user);
try {
const res = await fetchOk(url, fp.mergeAll([Config.authOpts(), Config.jsonBody(filteredUser), { method: 'PUT' }]));
const res = await fetchOk(
url,
fp.mergeAll([
Config.authOpts(),
Config.jsonBody(filteredUser),
{ method: 'PUT' },
])
);
if (res.ok) {
return res.json();
}
Expand All @@ -68,46 +92,63 @@ export const User = {
}
},

registerUser: async () => {
registerUser: async (): Promise<DuosUser> => {
const url = `${await getApiUrl()}/api/user`;
const res = await fetchOk(url, fp.mergeAll([Config.authOpts(), { method: 'POST' }]));
const res = await fetchOk(
url,
fp.mergeAll([Config.authOpts(), { method: 'POST' }])
);
return res.json();
},

getSOsForCurrentUser: async () => {
getSOsForCurrentUser: async (): Promise<SimplifiedDuosUser[]> => {
const url = `${await getApiUrl()}/api/user/signing-officials`;
const res = await fetchOk(url, fp.mergeAll([Config.authOpts(), { method: 'GET' }]));
const res = await fetchOk(
url,
fp.mergeAll([Config.authOpts(), { method: 'GET' }])
);
return res.json();
},

getUnassignedUsers: async () => {
getUnassignedUsers: async (): Promise<DuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/institution/unassigned`;
const res = await axios.get(url, Config.authOpts());
return res.data;
},

addRoleToUser: async (userId, roleId) => {

addRoleToUser: async (userId: number, roleId: number): Promise<DuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/${userId}/${roleId}`;
const res = await fetchAny(url, fp.mergeAll([Config.authOpts(), { method: 'PUT' }]));
const res = await fetchAny(
url,
fp.mergeAll([Config.authOpts(), { method: 'PUT' }])
);
return res.json();
},

deleteRoleFromUser: async (userId, roleId) => {
deleteRoleFromUser: async (userId: number, roleId: number): Promise<DuosUserResponse> => {
const url = `${await getApiUrl()}/api/user/${userId}/${roleId}`;
const res = await fetchAny(url, fp.mergeAll([Config.authOpts(), { method: 'DELETE' }]));
const res = await fetchAny(
url,
fp.mergeAll([Config.authOpts(), { method: 'DELETE' }])
);
return res.json();
},
getUserRelevantDatasets: async () => {


getUserRelevantDatasets: async (): Promise<DataSet[]> => {
const url = `${await getApiUrl()}/api/user/me/dac/datasets`;
const res = await axios.get(url, Config.authOpts());
return res.data;
},
getAcknowledgements: async () => {

getAcknowledgements: async (): Promise<AcknowledgementMap> => {
const url = `${await getApiUrl()}/api/user/acknowledgements`;
const res = await axios.get(url, Config.authOpts());
return res.data;
},
acceptAcknowledgments: async (...keys) => {

acceptAcknowledgments: async (...keys: string[]): Promise<AcknowledgementMap> => {
if (keys.length === 0) {
return {};
}
Expand All @@ -116,9 +157,10 @@ export const User = {
const res = await axios.post(url, keys, Config.authOpts());
return res.data;
},
getApprovedDatasets: async () => {

getApprovedDatasets: async (): Promise<ApprovedDataset> => {
const url = `${await getApiUrl()}/api/user/me/researcher/datasets`;
const res = await axios.get(url, Config.authOpts());
return res.data;
}
},
};
7 changes: 7 additions & 0 deletions src/libs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ export const convertLabelToKey = (label = '') => {
return label.split(/[\s,]+/).join('-');
};

/**
* Sets the user's role status.
* Converts a DuosUserResponse into a DuosUser.
* @param {DuosUserResponse} user
* @param {*} Storage
* @returns converted DuosUser
*/
export const setUserRoleStatuses = (user, Storage) => {
const currentUserRoles = (user.roles) ? user.roles.map(roles => roles.name) : [];
user.isChairPerson = currentUserRoles.indexOf(USER_ROLES.chairperson) > -1;
Expand Down
187 changes: 187 additions & 0 deletions src/types/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { DuosUserResponse } from './responseTypes';

export type UserRoleName =
| 'Admin'
| 'Chairperson'
| 'Member'
| 'Researcher'
| 'Alumni'
| 'SigningOfficial'
| 'DataSubmitter'
| 'All';
Copy link
Contributor

Choose a reason for hiding this comment

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

Technically, we don't have a back end role for 'All', but it might be helpful in this context if all means

Any role is allowed to access this resource


export interface UserRole {
roleId: number;
name: UserRoleName;
userId: number;
userRoleId: number;
}

export interface UserStatusInfo {
adminEnabled: boolean;
enabled: boolean;
userEmail: string;
userSubjectId: string;
}

export interface UserProperty {
eraAuthorized: string;
eraExpiration: string;
suggestedInstitution: string;
suggestedSigningOfficial: string;
selectedSigningOfficial: string;
daaAcceptance: string;
}

export interface DuosUser {
createDate: Date;
displayName: string;
email: string;
emailPreference: boolean;
isAdmin: boolean;
isAlumni: boolean;
isChairPerson: boolean;
isDataSubmitter: boolean;
isMember: boolean;
isResearcher: boolean;
isSigningOfficial: boolean;
libraryCards: LibraryCard[];
researcherProperties: UserProperty[];
roles: UserRole[];
userId: number;
userStatusInfo: UserStatusInfo;
}

export interface SimplifiedDuosUser {
userId: number;
displayName: string;
email: string;
}

export interface LibraryCard {
id: number;
userId: number;
institution: Institution;
institutionId: number;
eraCommonsId: string;
userName: string;
userEmail: string;
createDate: string;
createUserId: number;
updateDate: string;
updateUserId: number;
}

export type OrganizationType = 'For-Profit' | 'Nonprofit';

export interface Institution {
id: number;
name: string;
itDirectorName: string;
itDirectorEmail: string;
institutionUrl: string;
dunsNumber: number;
orgChartUrl: string;
verificationUrl: string;
verificationFilename: string;
organizationType: OrganizationType;
createUser: number;
createDate: Date;
updateUser: number;
updateDate: Date;
signingOfficials: SimplifiedDuosUser[];
}

export interface DataSet {
name: string;
datasetName: string;
dataSetId: number;
createUserId: number;
createUser: DuosUserResponse;
dacId: string;
consentId: string;
translatedDataUse: string;
deletable: boolean;
properties: DatasetProperty[];
study: Study;
active: boolean;
needsApproval: boolean;
alias: string;
datasetIdentifier: string;
objectId: string;
/**
* TODO: Determine what type this is
*/
dataUse: unknown;
dacApproval: boolean;
nihCertificationFile: FileStorageObject;
alternativeDataSharingPlanFile: FileStorageObject;
}

export interface DatasetProperty {
propertyName: string;
propertyValue: string;
}

export interface Study {
studyId: number;
name: string;
description: string;
dataTypes: string[];
piName: string;
publicVisibility: boolean;
datasetIds: number[];
datasets: DataSet[];
properties: StudyProperty[];
alternativeDataSharingPlan: FileStorageObject;
createDate: string; //Date?
createUserId: number;
updateDate: string; //Date?
updateUserId: number;
}

export interface StudyProperty {
key: string;
value: string;
type: string;
}

export type FileStorageCategory =
| 'irbCollaborationLetter'
| 'dataUseLetter'
| 'alternativeDataSharingPlan'
| 'nihInstitutionalCertification';

export interface FileStorageObject {
fileStorageObjectId: number;
entityId: string;
fileName: string;
category: FileStorageCategory;
mediaType: string;
createUserId: number;
createDate: number;
updateUserId: number;
updateDate: number;
deleteUserId: number;
deleteDate: number;
deleted: boolean;
}

export interface ApprovedDataset {
darId: string;
datasetId: number;
datasetName: string;
dacName: string;
approvalDate: string;
}

export interface AcknowledgementMap {
[key: string]: Acknowledgement;
}

export interface Acknowledgement {
userId: number;
ackKey: string;
firstAcknowledged: number;
lastAcknowledged: number;
}
Loading
Loading