@openmrs/esm-api 10.0.1-pre.4892 → 10.0.1-pre.4900

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.
@@ -1,3 +1,3 @@
1
- [0] Successfully compiled: 14 files with swc (101.13ms)
1
+ [0] Successfully compiled: 14 files with swc (92.21ms)
2
2
  [0] swc --strip-leading-paths src -d dist exited with code 0
3
3
  [1] tsc --project tsconfig.build.json exited with code 0
@@ -1 +1 @@
1
- {"version":3,"file":"openmrs-fetch.d.ts","sourceRoot":"","sources":["../src/openmrs-fetch.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,mEAAmE;AACnE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,uEAAuE;AACvE,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAC1C,sDAAsD;AACtD,eAAO,MAAM,eAAe,wBAA2B,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,UASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CA8J1G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,gCA8BjF;AAED,qBAAa,iBAAkB,SAAQ,KAAM,YAAW,UAAU;gBACpD,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,EAAE,iBAAiB,EAAE,KAAK;IAQxG,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACxE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC"}
1
+ {"version":3,"file":"openmrs-fetch.d.ts","sourceRoot":"","sources":["../src/openmrs-fetch.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAIlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAG7C,mEAAmE;AACnE,eAAO,MAAM,WAAW,gBAAgB,CAAC;AACzC,uEAAuE;AACvE,eAAO,MAAM,WAAW,iBAAiB,CAAC;AAC1C,sDAAsD;AACtD,eAAO,MAAM,eAAe,wBAA2B,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,UASnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAuK1G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,WAAgB,gCA8BjF;AAED,qBAAa,iBAAkB,SAAQ,KAAM,YAAW,UAAU;gBACpD,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,EAAE,iBAAiB,EAAE,KAAK;IAQxG,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACxE,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B;AAED,KAAK,YAAY,GAAG,MAAM,GAAG,iBAAiB,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,SAAS;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC"}
@@ -143,15 +143,20 @@ import { defaultRedirectAuthFailureUrl } from "./config-schema.js";
143
143
  const response = r;
144
144
  const { redirectAuthFailure, followRedirects } = await getConfig('@openmrs/esm-api');
145
145
  if (response.ok) {
146
+ /*
147
+ * Backend modules can trigger SPA redirects by returning a `Location` header.
148
+ * This is required because `fetch()` hides HTTP redirects from the application.
149
+ *
150
+ * - Session endpoint (2xx): Authentication challenge URLs (e.g. TOTP,2FA).
151
+ * - HTTP 204: Logout redirect URLs (e.g. Keycloak IdP logout). Refer: OA-41 #1231
152
+ */ const location = response.headers.get('location');
153
+ const shouldRedirect = followRedirects && location && (url === makeUrl(sessionEndpoint) || response.status === 204);
154
+ if (shouldRedirect) {
155
+ navigate({
156
+ to: location
157
+ });
158
+ }
146
159
  if (response.status === 204) {
147
- if (followRedirects && response.headers.has('location')) {
148
- const location = response.headers.get('location');
149
- if (location) {
150
- navigate({
151
- to: location
152
- });
153
- }
154
- }
155
160
  /* HTTP 204 - No Content
156
161
  * We should not try to download the empty response as json. Instead,
157
162
  * we return null since there is no response body.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-api",
3
- "version": "10.0.1-pre.4892",
3
+ "version": "10.0.1-pre.4900",
4
4
  "license": "MPL-2.0",
5
5
  "description": "The javascript module for interacting with the OpenMRS API",
6
6
  "type": "module",
@@ -56,16 +56,16 @@
56
56
  "lodash-es": "^4.17.21"
57
57
  },
58
58
  "peerDependencies": {
59
- "@openmrs/esm-config": "^10.0.1-pre.4892",
60
- "@openmrs/esm-error-handling": "^10.0.1-pre.4892",
61
- "@openmrs/esm-globals": "^10.0.1-pre.4892",
62
- "@openmrs/esm-navigation": "^10.0.1-pre.4892"
59
+ "@openmrs/esm-config": "^10.0.1-pre.4900",
60
+ "@openmrs/esm-error-handling": "^10.0.1-pre.4900",
61
+ "@openmrs/esm-globals": "^10.0.1-pre.4900",
62
+ "@openmrs/esm-navigation": "^10.0.1-pre.4900"
63
63
  },
64
64
  "devDependencies": {
65
- "@openmrs/esm-config": "10.0.1-pre.4892",
66
- "@openmrs/esm-error-handling": "10.0.1-pre.4892",
67
- "@openmrs/esm-globals": "10.0.1-pre.4892",
68
- "@openmrs/esm-navigation": "10.0.1-pre.4892",
65
+ "@openmrs/esm-config": "10.0.1-pre.4900",
66
+ "@openmrs/esm-error-handling": "10.0.1-pre.4900",
67
+ "@openmrs/esm-globals": "10.0.1-pre.4900",
68
+ "@openmrs/esm-navigation": "10.0.1-pre.4900",
69
69
  "@swc/cli": "0.8.1",
70
70
  "@swc/core": "1.15.21",
71
71
  "@vitest/coverage-v8": "^4.1.2",
@@ -22,6 +22,7 @@ describe('openmrsFetch', () => {
22
22
  errors: [401],
23
23
  resolvePromise: false,
24
24
  },
25
+ followRedirects: true,
25
26
  }),
26
27
  );
27
28
  window.openmrsBase = '/openmrs';
@@ -131,6 +132,10 @@ describe('openmrsFetch', () => {
131
132
  Promise.resolve({
132
133
  ok: true,
133
134
  status: 200,
135
+ headers: {
136
+ has: () => false,
137
+ get: () => null,
138
+ },
134
139
  clone: () => ({
135
140
  text: () => Promise.resolve('{ "value": "hi" }'),
136
141
  }),
@@ -148,6 +153,10 @@ describe('openmrsFetch', () => {
148
153
  Promise.resolve({
149
154
  ok: true,
150
155
  status: 204,
156
+ headers: {
157
+ has: () => false,
158
+ get: () => null,
159
+ },
151
160
  json: () => Promise.reject(Error("No json for HTTP 204's!!")),
152
161
  }),
153
162
  );
@@ -241,6 +250,33 @@ describe('openmrsFetch', () => {
241
250
  });
242
251
  });
243
252
 
253
+ it('redirects to the Location header URL when session endpoint called and it contains a Location header', async () => {
254
+ // @ts-expect-error
255
+ window.fetch.mockReturnValue(
256
+ Promise.resolve({
257
+ ok: true,
258
+ status: 200,
259
+ statusText: 'OK',
260
+ headers: {
261
+ has: (name: string) => name.toLowerCase() === 'location',
262
+ get: (name: string) => (name.toLowerCase() === 'location' ? '/openmrs/spa/login' : null),
263
+ },
264
+ clone() {
265
+ return this;
266
+ },
267
+ text: () => Promise.resolve(''),
268
+ }),
269
+ );
270
+
271
+ const fetchPromise = openmrsFetch('/ws/rest/v1/session');
272
+
273
+ await new Promise((resolve) => setTimeout(resolve, 0));
274
+
275
+ expect(mockNavigate).toHaveBeenCalledWith({
276
+ to: '/openmrs/spa/login',
277
+ });
278
+ });
279
+
244
280
  it('redirects to the default login URL when a 401 response has no Location header (genuine auth failure)', async () => {
245
281
  mockGetConfig.mockResolvedValueOnce({
246
282
  redirectAuthFailure: {
@@ -312,6 +348,10 @@ describe('openmrsObservableFetch', () => {
312
348
  Promise.resolve({
313
349
  ok: true,
314
350
  status: 200,
351
+ headers: {
352
+ has: () => false,
353
+ get: () => null,
354
+ },
315
355
  clone: () => ({
316
356
  text: () => Promise.resolve('{"value": "hi"}'),
317
357
  }),
@@ -157,15 +157,24 @@ export function openmrsFetch<T = any>(path: string, fetchInit: FetchConfig = {})
157
157
  return window.fetch(url, fetchInit as RequestInit).then(async (r) => {
158
158
  const response = r as FetchResponse<T>;
159
159
  const { redirectAuthFailure, followRedirects } = await getConfig<EsmApiConfigObject>('@openmrs/esm-api');
160
+
160
161
  if (response.ok) {
161
- if (response.status === 204) {
162
- if (followRedirects && response.headers.has('location')) {
163
- const location = response.headers.get('location');
164
- if (location) {
165
- navigate({ to: location });
166
- }
167
- }
162
+ /*
163
+ * Backend modules can trigger SPA redirects by returning a `Location` header.
164
+ * This is required because `fetch()` hides HTTP redirects from the application.
165
+ *
166
+ * - Session endpoint (2xx): Authentication challenge URLs (e.g. TOTP,2FA).
167
+ * - HTTP 204: Logout redirect URLs (e.g. Keycloak IdP logout). Refer: OA-41 #1231
168
+ */
169
+ const location = response.headers.get('location');
170
+ const shouldRedirect =
171
+ followRedirects && location && (url === makeUrl(sessionEndpoint) || response.status === 204);
168
172
 
173
+ if (shouldRedirect) {
174
+ navigate({ to: location });
175
+ }
176
+
177
+ if (response.status === 204) {
169
178
  /* HTTP 204 - No Content
170
179
  * We should not try to download the empty response as json. Instead,
171
180
  * we return null since there is no response body.