@openmrs/esm-api 3.3.2-pre.1187 → 3.3.2-pre.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-api",
3
- "version": "3.3.2-pre.1187",
3
+ "version": "3.3.2-pre.12",
4
4
  "license": "MPL-2.0",
5
5
  "description": "The javascript module for interacting with the OpenMRS API",
6
6
  "browser": "dist/openmrs-esm-api.js",
@@ -44,11 +44,11 @@
44
44
  "@openmrs/esm-error-handling": "3.x"
45
45
  },
46
46
  "devDependencies": {
47
- "@openmrs/esm-config": "^3.3.2-pre.1187",
48
- "@openmrs/esm-error-handling": "^3.3.2-pre.1187",
49
- "@openmrs/esm-state": "^3.3.2-pre.1187",
47
+ "@openmrs/esm-config": "^3.3.2-pre.12",
48
+ "@openmrs/esm-error-handling": "^3.3.2-pre.12",
49
+ "@openmrs/esm-state": "^3.3.2-pre.12",
50
50
  "@types/fhir": "0.0.31",
51
51
  "rxjs": "^6.5.3"
52
52
  },
53
- "gitHead": "6739e87e1c98a4b3b1d7471b44c06a016d2cfff6"
53
+ "gitHead": "6fd3b201670a9aa367c65548cb4da75491095f4c"
54
54
  }
@@ -1,7 +1,12 @@
1
1
  /** @module @category API */
2
2
  import { Observable } from "rxjs";
3
3
  import isPlainObject from "lodash-es/isPlainObject";
4
- import { getConfig, navigate } from "@openmrs/esm-config";
4
+ import {
5
+ getConfig,
6
+ interpolateString,
7
+ interpolateUrl,
8
+ navigate,
9
+ } from "@openmrs/esm-config";
5
10
  import { FetchResponse } from "./types";
6
11
 
7
12
  export const sessionEndpoint = "/ws/rest/v1/session";
@@ -1,19 +1,32 @@
1
1
  /** @module @category API */
2
- import { Observable, ReplaySubject } from "rxjs";
3
- import { filter, map, tap, mergeAll } from "rxjs/operators";
2
+ import { reportError } from "@openmrs/esm-error-handling";
3
+ import { createGlobalStore } from "@openmrs/esm-state";
4
+ import { Observable } from "rxjs";
4
5
  import { openmrsFetch, sessionEndpoint } from "../openmrs-fetch";
5
6
  import type {
6
7
  LoggedInUser,
7
- CurrentUserWithResponseOption,
8
- CurrentUserWithoutResponseOption,
9
- CurrentUserOptions,
10
8
  SessionLocation,
11
9
  Privilege,
12
10
  Role,
13
11
  Session,
14
12
  } from "../types";
15
13
 
16
- const userSubject = new ReplaySubject<Promise<Session>>(1);
14
+ export type SessionStore = LoadedSessionStore | UnloadedSessionStore;
15
+
16
+ export type LoadedSessionStore = {
17
+ loaded: true;
18
+ session: Session;
19
+ };
20
+
21
+ export type UnloadedSessionStore = {
22
+ loaded: false;
23
+ session: null;
24
+ };
25
+
26
+ const sessionStore = createGlobalStore<SessionStore>("session", {
27
+ loaded: false,
28
+ session: null,
29
+ });
17
30
  let lastFetchTimeMillis = 0;
18
31
 
19
32
  /**
@@ -54,30 +67,51 @@ let lastFetchTimeMillis = 0;
54
67
  * even after the UI component is gone from the screen. This is a memory
55
68
  * leak and source of bugs.
56
69
  */
57
- function getCurrentUser(): Observable<LoggedInUser>;
58
- function getCurrentUser(
59
- opts: CurrentUserWithResponseOption
60
- ): Observable<Session>;
61
- function getCurrentUser(
62
- opts: CurrentUserWithoutResponseOption
63
- ): Observable<LoggedInUser>;
70
+ function getCurrentUser(): Observable<Session>;
71
+ function getCurrentUser(opts: { includeAuthStatus: true }): Observable<Session>;
72
+ function getCurrentUser(opts: {
73
+ includeAuthStatus: false;
74
+ }): Observable<LoggedInUser>;
64
75
  function getCurrentUser(
65
- opts: CurrentUserOptions = { includeAuthStatus: false }
66
- ): Observable<LoggedInUser | Session> {
67
- if (lastFetchTimeMillis < Date.now() - 1000 * 60) {
76
+ opts = { includeAuthStatus: true }
77
+ ): Observable<Session | LoggedInUser> {
78
+ if (
79
+ lastFetchTimeMillis < Date.now() - 1000 * 60 ||
80
+ !sessionStore.getState().loaded
81
+ ) {
68
82
  refetchCurrentUser();
69
83
  }
70
84
 
71
- return userSubject.asObservable().pipe(
72
- mergeAll(),
73
- tap(setUserLanguage),
74
- map((r) => (opts.includeAuthStatus ? r : r.user)),
75
- filter(Boolean)
76
- ) as Observable<LoggedInUser | Session>;
85
+ return new Observable((subscriber) => {
86
+ const handler = (state) => {
87
+ if (state.loaded) {
88
+ if (opts.includeAuthStatus) {
89
+ subscriber.next(state.session);
90
+ } else {
91
+ subscriber.next(state.session?.user);
92
+ }
93
+ }
94
+ };
95
+ handler(sessionStore.getState());
96
+ // The observable subscribe function should return an unsubscribe function,
97
+ // which happens to be exactly what Unistore `subscribe` returns.
98
+ return sessionStore.subscribe(handler);
99
+ });
77
100
  }
78
101
 
79
102
  export { getCurrentUser };
80
103
 
104
+ export function getSessionStore() {
105
+ if (
106
+ lastFetchTimeMillis < Date.now() - 1000 * 60 ||
107
+ !sessionStore.getState().loaded
108
+ ) {
109
+ refetchCurrentUser();
110
+ }
111
+
112
+ return sessionStore;
113
+ }
114
+
81
115
  function setUserLanguage(data: Session) {
82
116
  const locale = data?.user?.userProperties?.defaultLocale ?? data.locale;
83
117
  const htmlLang = document.documentElement.getAttribute("lang");
@@ -113,17 +147,35 @@ function isSuperUser(user: { roles: Array<Role> }) {
113
147
  * ```
114
148
  */
115
149
  export function refetchCurrentUser() {
116
- lastFetchTimeMillis = Date.now();
117
- userSubject.next(
150
+ return new Promise((resolve, reject) => {
151
+ lastFetchTimeMillis = Date.now();
118
152
  openmrsFetch(sessionEndpoint)
119
- .then((res) =>
120
- typeof res.data === "object" ? res.data : Promise.reject()
121
- )
122
- .catch(() => ({
123
- sessionId: "",
124
- authenticated: false,
125
- }))
126
- );
153
+ .then((res) => {
154
+ if (typeof res?.data === "object") {
155
+ setUserLanguage(res.data);
156
+ resolve(res.data);
157
+ sessionStore.setState({ loaded: true, session: res.data });
158
+ } else {
159
+ reject();
160
+ return Promise.reject();
161
+ }
162
+ })
163
+ .catch((err) => {
164
+ reportError(`Failed to fetch new session information: ${err}`);
165
+ reject(err);
166
+ return {
167
+ sessionId: "",
168
+ authenticated: false,
169
+ };
170
+ });
171
+ });
172
+ }
173
+
174
+ export function clearCurrentUser() {
175
+ sessionStore.setState({
176
+ loaded: true,
177
+ session: { authenticated: false, sessionId: "" },
178
+ });
127
179
  }
128
180
 
129
181
  export function userHasAccess(
@@ -134,23 +186,29 @@ export function userHasAccess(
134
186
  }
135
187
 
136
188
  export function getLoggedInUser() {
189
+ let user;
190
+ let unsubscribe;
137
191
  return new Promise<LoggedInUser>((res, rej) => {
138
- const sub = getCurrentUser().subscribe((user) => {
139
- res(user);
140
- sub.unsubscribe();
141
- }, rej);
192
+ const handler = (state: SessionStore) => {
193
+ if (state.loaded && state.session.user) {
194
+ user = state.session.user;
195
+ res(state.session.user);
196
+ unsubscribe && unsubscribe();
197
+ }
198
+ };
199
+ handler(sessionStore.getState());
200
+ if (!user) {
201
+ unsubscribe = sessionStore.subscribe(handler);
202
+ }
142
203
  });
143
204
  }
144
205
 
145
206
  export function getSessionLocation() {
146
207
  return new Promise<SessionLocation | undefined>((res, rej) => {
147
- const sub = getCurrentUser({ includeAuthStatus: true }).subscribe(
148
- (session) => {
149
- res(session.sessionLocation);
150
- sub.unsubscribe();
151
- },
152
- rej
153
- );
208
+ const sub = getCurrentUser().subscribe((session) => {
209
+ res(session.sessionLocation);
210
+ sub.unsubscribe();
211
+ }, rej);
154
212
  });
155
213
  }
156
214
 
@@ -1,15 +1,3 @@
1
- export interface CurrentUserOptions {
2
- includeAuthStatus?: boolean;
3
- }
4
-
5
- export interface CurrentUserWithResponseOption extends CurrentUserOptions {
6
- includeAuthStatus: true;
7
- }
8
-
9
- export interface CurrentUserWithoutResponseOption extends CurrentUserOptions {
10
- includeAuthStatus: false;
11
- }
12
-
13
1
  export interface Session {
14
2
  allowedLocales?: Array<string>;
15
3
  authenticated: boolean;