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) Unify session types #387

Merged
merged 1 commit into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions packages/apps/esm-login-app/src/CurrentUserContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useContext, useState, useMemo, useEffect } from "react";
import { getCurrentUser } from "@openmrs/esm-framework";
import { getCurrentUser, LoggedInUser } from "@openmrs/esm-framework";

const CurrentUser = React.createContext<User>({
current: undefined,
Expand All @@ -16,26 +16,6 @@ export interface User {
setCurrent(user: LoggedInUser): void;
}

export interface ResourceRef {
uuid: string;
display: string;
links: Array<any>;
}

export interface LoggedInUser {
uuid: string;
display: string;
username: string;
systemId: string;
userProperties: any;
person: ResourceRef;
privileges: Array<ResourceRef>;
roles: Array<ResourceRef>;
retired: boolean;
locale: string;
allowedLocales: Array<string>;
}

export function useCurrentUser() {
const value = useContext(CurrentUser);
return value.current;
Expand Down
8 changes: 3 additions & 5 deletions packages/apps/esm-offline-tools-app/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"offlineActionsTableAction": "Action",
"offlineActionsTableCreatedOn": "Date & Time",
"offlineActionsTableDeleteAction": "Delete action",
"offlineActionsTableDeleteActions": "Delete {count} actions",
"offlineActionsTableDeleteActions_plural": "Delete {count} actions",
"offlineActionsTableDeleteActions_one": "Delete {count} actions",
"offlineActionsTableDeleteActions_other": "Delete {count} actions",
"offlineActionsTableError": "Error",
"offlineActionsTablePatient": "Patient",
"offlineActionsUpdateOfflinePatients": "Update offline patients",
Expand All @@ -32,7 +32,5 @@
"offlinePatientSyncDetailsFallbackErrorMessage": "Unknown error.",
"offlinePatientSyncDetailsHeader": "Offline patient details",
"offlineReady": "Offline Ready",
"offlineToolsAppMenuLink": "Offline tools",
"offlineActionsTableDeleteActions_one": "Delete {count} actions",
"offlineActionsTableDeleteActions_other": "Delete {count} actions"
"offlineToolsAppMenuLink": "Offline tools"
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ export interface RootProps {}
const Root: React.FC<RootProps> = () => {
const [user, setUser] = useState<LoggedInUser | null | false>(null);
const [userSession, setUserSession] = useState<UserSession>(null);
const [allowedLocales, setAllowedLocales] = useState();
const [allowedLocales, setAllowedLocales] = useState<Array<string> | null>();
const logout = useCallback(() => setUser(false), []);
const openmrsSpaBase = window["getOpenmrsSpaBase"]();

useEffect(() => {
const currentUserSub = getSynchronizedCurrentUser({
includeAuthStatus: true,
}).subscribe((response) => {
setAllowedLocales(response["allowedLocales"]);
setAllowedLocales(response.allowedLocales);

if (response.authenticated) {
setUser(response.user);
} else {
Expand Down
48 changes: 27 additions & 21 deletions packages/framework/esm-api/src/shared-api-objects/current-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
import { Observable, ReplaySubject } from "rxjs";
import { filter, map, tap, mergeAll } from "rxjs/operators";
import { openmrsFetch, sessionEndpoint } from "../openmrs-fetch";
import {
LoggedInUserData,
import type {
LoggedInUser,
CurrentUserWithResponseOption,
UnauthenticatedUser,
CurrentUserWithoutResponseOption,
CurrentUserOptions,
SessionLocation,
Privilege,
Role,
Session,
} from "../types";

const userSubject = new ReplaySubject<Promise<LoggedInUserData>>(1);
const userSubject = new ReplaySubject<Promise<Session>>(1);
let lastFetchTimeMillis = 0;

/**
Expand Down Expand Up @@ -56,13 +57,13 @@ let lastFetchTimeMillis = 0;
function getCurrentUser(): Observable<LoggedInUser>;
function getCurrentUser(
opts: CurrentUserWithResponseOption
): Observable<UnauthenticatedUser>;
): Observable<Session>;
function getCurrentUser(
opts: CurrentUserWithoutResponseOption
): Observable<LoggedInUser>;
function getCurrentUser(
opts: CurrentUserOptions = { includeAuthStatus: false }
): Observable<LoggedInUser | UnauthenticatedUser> {
): Observable<LoggedInUser | Session> {
if (lastFetchTimeMillis < Date.now() - 1000 * 60) {
refetchCurrentUser();
}
Expand All @@ -72,31 +73,32 @@ function getCurrentUser(
tap(setUserLanguage),
map((r) => (opts.includeAuthStatus ? r : r.user)),
filter(Boolean)
) as Observable<LoggedInUser | UnauthenticatedUser>;
) as Observable<LoggedInUser | Session>;
}

function setUserLanguage(data: LoggedInUserData) {
if (data?.user?.userProperties?.defaultLocale) {
const locale = data.user.userProperties.defaultLocale;
const htmlLang = document.documentElement.getAttribute("lang");
export { getCurrentUser };

function setUserLanguage(data: Session) {
const locale = data?.user?.userProperties?.defaultLocale ?? data.locale;
const htmlLang = document.documentElement.getAttribute("lang");

if (locale !== htmlLang) {
document.documentElement.setAttribute("lang", locale);
}
if (locale !== htmlLang) {
document.documentElement.setAttribute("lang", locale);
}
}

function userHasPrivilege(requiredPrivilege: string, user: LoggedInUser) {
function userHasPrivilege(
requiredPrivilege: string,
user: { privileges: Array<Privilege> }
) {
return user.privileges.find((p) => requiredPrivilege === p.display);
}

function isSuperUser(user: LoggedInUser) {
function isSuperUser(user: { roles: Array<Role> }) {
const superUserRole = "System Developer";
return user.roles.find((role) => role.display === superUserRole);
}

export { getCurrentUser };

/**
* The `refetchCurrentUser` function causes a network request to redownload
* the user. All subscribers to the current user will be notified of the
Expand Down Expand Up @@ -124,7 +126,10 @@ export function refetchCurrentUser() {
);
}

export function userHasAccess(requiredPrivilege: string, user: LoggedInUser) {
export function userHasAccess(
requiredPrivilege: string,
user: { privileges: Array<Privilege>; roles: Array<Role> }
) {
return userHasPrivilege(requiredPrivilege, user) || isSuperUser(user);
}

Expand All @@ -140,8 +145,8 @@ export function getLoggedInUser() {
export function getSessionLocation() {
return new Promise<SessionLocation | undefined>((res, rej) => {
const sub = getCurrentUser({ includeAuthStatus: true }).subscribe(
(user) => {
res(user.sessionLocation);
(session) => {
res(session.sessionLocation);
sub.unsubscribe();
},
rej
Expand All @@ -161,5 +166,6 @@ export async function setSessionLocation(
},
signal: abortController.signal,
});

refetchCurrentUser();
}
8 changes: 2 additions & 6 deletions packages/framework/esm-api/src/types/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { LoggedInUser, UnauthenticatedUser } from "./user-resource";
import { Session } from "./user-resource";

export interface FetchResponse<T = any> extends Response {
data: T;
}

export type LoggedInUserData = UnauthenticatedUser & {
user?: LoggedInUser;
};

export interface LoggedInUserFetchResponse extends FetchResponse {
data: LoggedInUserData;
data: Session;
}
22 changes: 0 additions & 22 deletions packages/framework/esm-api/src/types/openmrs-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,3 @@ export interface OpenmrsResource {
display?: string;
[anythingElse: string]: any;
}

export interface SessionUser {
allowedLocales: Array<string>;
authenticated: boolean;
locale: string;
sessionId: string;
user: User;
currentProvider: { uuid: string; identifier: string };
sessionLocation: any | null;
}

export interface User {
display: string;
link: Array<string>;
person: any;
priviliges: any;
resourceVersion: any;
roles: Array<any>;
userProperties: any;
username: string;
uuid: string;
}
21 changes: 12 additions & 9 deletions packages/framework/esm-api/src/types/user-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ export interface CurrentUserWithoutResponseOption extends CurrentUserOptions {
includeAuthStatus: false;
}

export interface Session {
allowedLocales?: Array<string>;
authenticated: boolean;
locale?: string;
sessionId: string;
user?: LoggedInUser;
currentProvider?: { uuid: string; identifier: string };
sessionLocation?: SessionLocation;
}

export interface LoggedInUser {
uuid: string;
display: string;
username: string;
systemId: string;
userProperties: any;
userProperties: { [key: string]: any } | null;
person: Person;
privileges: Array<Privilege>;
roles: Array<Role>;
Expand All @@ -25,13 +35,6 @@ export interface LoggedInUser {
[anythingElse: string]: any;
}

export interface UnauthenticatedUser {
sessionId: string;
authenticated: boolean;
user?: LoggedInUser;
sessionLocation?: SessionLocation;
}

export interface SessionLocation {
uuid: string;
display: string;
Expand All @@ -47,7 +50,7 @@ export interface Person {
export interface Privilege {
uuid: string;
display: string;
links: Array<any>;
links?: Array<any>;
}

export interface Role {
Expand Down
Loading