@overmap-ai/core 1.0.60-sdk-refactor.3 → 1.0.60-sdk-refactor.5

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.
@@ -35,8 +35,8 @@ import createMigration from "redux-persist-migrate";
35
35
  import { createSlice, createSelector, combineReducers, createNextState } from "@reduxjs/toolkit";
36
36
  import request from "superagent";
37
37
  import { shallowEqual as shallowEqual$1 } from "react-redux";
38
- import jwtDecode from "jwt-decode";
39
38
  import { RESET_STATE } from "@redux-offline/redux-offline/lib/constants";
39
+ import jwtDecode from "jwt-decode";
40
40
  import { openDB } from "idb";
41
41
  var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
42
42
  HttpMethod2["GET"] = "GET";
@@ -13710,7 +13710,7 @@ const selectOrganization = (id) => (state) => {
13710
13710
  return state.organizationReducer.organizations[id];
13711
13711
  };
13712
13712
  const organizationReducer = organizationSlice.reducer;
13713
- const createOfflineAction = (request2, baseUrl) => {
13713
+ const createOfflineAction = (request2, baseUrl, serviceName) => {
13714
13714
  const requestWithUuid = request2.uuid ? request2 : { ...request2, uuid: v4() };
13715
13715
  return {
13716
13716
  payload: requestWithUuid,
@@ -13720,7 +13720,8 @@ const createOfflineAction = (request2, baseUrl) => {
13720
13720
  effect: {
13721
13721
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13722
13722
  request: requestWithUuid,
13723
- BASE_URL: baseUrl
13723
+ BASE_URL: baseUrl,
13724
+ serviceName
13724
13725
  }
13725
13726
  }
13726
13727
  }
@@ -13744,8 +13745,8 @@ const outboxSlice = createSlice({
13744
13745
  },
13745
13746
  prepare: (payload) => {
13746
13747
  console.debug("Preparing to enqueue request", payload);
13747
- const { BASE_URL, ...rest } = payload;
13748
- return createOfflineAction(rest, BASE_URL);
13748
+ const { BASE_URL, serviceName, ...rest } = payload;
13749
+ return createOfflineAction(rest, BASE_URL, serviceName);
13749
13750
  }
13750
13751
  },
13751
13752
  markForDeletion(state, action) {
@@ -15066,24 +15067,16 @@ function extractResponseFromError(error2) {
15066
15067
  return void 0;
15067
15068
  }
15068
15069
  async function performRequest(action, client) {
15069
- async function checkToken() {
15070
- if (client.auth.tokenIsExpiringSoon()) {
15071
- await client.auth.renewTokens();
15072
- }
15070
+ const serviceOfRequest = CLASS_NAME_TO_SERVICE[action.meta.offline.effect.serviceName];
15071
+ if (!serviceOfRequest) {
15072
+ throw new Error(`Service ${action.meta.offline.effect.serviceName} not found`);
15073
15073
  }
15074
15074
  const state = client.store.getState();
15075
15075
  if (state.outboxReducer.deletedRequests.includes(action.payload.uuid)) {
15076
15076
  throw new Error("Request was marked for deletion");
15077
15077
  }
15078
15078
  if (action.payload.checkAuth !== false) {
15079
- try {
15080
- await checkToken();
15081
- } catch (e) {
15082
- if (e instanceof APIError) {
15083
- await client.auth.logout();
15084
- return Promise.reject(e);
15085
- }
15086
- }
15079
+ await serviceOfRequest.auth.prepareAuth();
15087
15080
  }
15088
15081
  const defaultSettings = {
15089
15082
  queryParams: "",
@@ -15097,7 +15090,6 @@ async function performRequest(action, client) {
15097
15090
  const requestDetails = offlineEffect.request;
15098
15091
  let url = requestDetails.url;
15099
15092
  const file = attachmentHash ? await client.files.fetchCache(attachmentHash) : void 0;
15100
- const accessToken = selectAccessToken(state);
15101
15093
  if (attachmentHash && !file) {
15102
15094
  throw new Error(`Cannot upload file ${attachmentHash} because it's not cached.`);
15103
15095
  }
@@ -15150,7 +15142,8 @@ async function performRequest(action, client) {
15150
15142
  const selectedRequest = methodRequestMapping[method];
15151
15143
  let requestToSend = selectedRequest();
15152
15144
  if (isAuthNeeded) {
15153
- requestToSend = requestToSend.set("Authorization", `Bearer ${accessToken}`);
15145
+ const authHeader = serviceOfRequest.auth.getAuthHeader();
15146
+ requestToSend = requestToSend.set("Authorization", authHeader);
15154
15147
  }
15155
15148
  if (headers) {
15156
15149
  requestToSend = requestToSend.set(headers);
@@ -15161,32 +15154,8 @@ async function performRequest(action, client) {
15161
15154
  const errorResponse = extractResponseFromError(error2);
15162
15155
  const status = errorResponse == null ? void 0 : errorResponse.status;
15163
15156
  if (status === 401) {
15164
- if (url.endsWith("/token/refresh/")) {
15165
- if (state.authReducer.isLoggedIn) {
15166
- console.warn("No signed-in user to sign out.");
15167
- }
15168
- await client.auth.logout();
15169
- throw new APIError({
15170
- message: "You have been signed out due to inactivity.",
15171
- response: errorResponse,
15172
- discard: true,
15173
- innerError: error2
15174
- });
15175
- }
15176
- if (state.authReducer.isLoggedIn) {
15177
- console.debug("Forbidden; renewing tokens and retrying.");
15178
- await client.auth.renewTokens();
15179
- console.debug("Successfully renewed tokens; retrying request.");
15180
- return requestToSend.query(queryParams);
15181
- } else {
15182
- console.debug("Forbidden; user is not logged in.");
15183
- throw new APIError({
15184
- message: "Incorrect username or password.",
15185
- response: errorResponse,
15186
- discard: true,
15187
- innerError: error2
15188
- });
15189
- }
15157
+ await serviceOfRequest.auth.handleUnauthorized(requestToSend, errorResponse);
15158
+ return requestToSend.query(queryParams);
15190
15159
  }
15191
15160
  throw new APIError({ response: errorResponse, innerError: error2, discard: discardStatuses.includes(status) });
15192
15161
  }
@@ -15359,27 +15328,17 @@ class BaseSDK {
15359
15328
  __publicField(this, "store");
15360
15329
  this.store = store;
15361
15330
  }
15362
- /**
15363
- * Enqueues an API request to the offline outbox.
15364
- * @param requestDetails An SDKRequest object containing the details of the request.
15365
- * @param host The base URL of the API to send the request to.
15366
- * @protected
15367
- */
15368
- async enqueueRequest(requestDetails, host) {
15369
- return this._enqueueRequest(requestDetails, host).then((result) => {
15331
+ async enqueueRequest(requestDetails, host, serviceName) {
15332
+ return this._enqueueRequest(requestDetails, host, serviceName).then((result) => {
15370
15333
  if (result instanceof APIError) {
15371
15334
  throw result;
15372
15335
  }
15373
15336
  return result;
15374
15337
  });
15375
15338
  }
15376
- /**
15377
- * Enqueues an API request to the Redux Offline outbox
15378
- * @protected
15379
- */
15380
- _enqueueRequest(requestDetails, host) {
15339
+ _enqueueRequest(requestDetails, host, serviceName) {
15381
15340
  const promise = new DeferredPromise();
15382
- const requestDetailsWithBaseUrl = { ...requestDetails, BASE_URL: host };
15341
+ const requestDetailsWithBaseUrl = { ...requestDetails, BASE_URL: host, serviceName };
15383
15342
  if (requestDetails.immediate) {
15384
15343
  const requestWithUuid = {
15385
15344
  ...requestDetailsWithBaseUrl,
@@ -15393,7 +15352,8 @@ class BaseSDK {
15393
15352
  effect: {
15394
15353
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
15395
15354
  request: requestWithUuid,
15396
- BASE_URL: host
15355
+ BASE_URL: host,
15356
+ serviceName
15397
15357
  }
15398
15358
  }
15399
15359
  }
@@ -15449,18 +15409,13 @@ const initSDK = (store, sdk) => {
15449
15409
  setClientStore(store);
15450
15410
  return sdkInstance;
15451
15411
  };
15452
- class BaseApiService {
15412
+ class BaseAuthService {
15453
15413
  constructor(sdk) {
15454
15414
  __publicField(this, "client");
15455
15415
  this.client = sdk;
15456
15416
  }
15457
- /**
15458
- * Enqueues an API request to the offline outbox.
15459
- * @param requestDetails An SDKRequest object containing the details of the request.
15460
- * @protected
15461
- */
15462
15417
  async enqueueRequest(requestDetails) {
15463
- return this.client.enqueueRequest(requestDetails, this.host);
15418
+ return this.client.enqueueRequest(requestDetails, this.host, this.constructor.name);
15464
15419
  }
15465
15420
  dispatch(action) {
15466
15421
  this.client.store.dispatch(action);
@@ -15474,42 +15429,12 @@ function parseTokens(response) {
15474
15429
  throw new Error("Missing refresh token");
15475
15430
  return { accessToken: response.access, refreshToken: response.refresh };
15476
15431
  }
15477
- class AuthService extends BaseApiService {
15432
+ class JWTService extends BaseAuthService {
15478
15433
  constructor() {
15479
15434
  super(...arguments);
15480
- __publicField(this, "_getAccessToken", () => this.client.store.getState().authReducer.accessToken);
15481
- __publicField(this, "_getRefreshToken", () => this.client.store.getState().authReducer.refreshToken);
15482
15435
  // _getTokenPair and _getRenewedTokens don't need to use enqueueRequest from the BaseApiService because
15483
15436
  // they are very simple. However, if we need robust error handling or want these operations to queue in the Outbox,
15484
15437
  // we will use enqueueRequest.
15485
- /**
15486
- * Takes credentials and gets a token pair
15487
- * @async
15488
- * @param credentials The username and password for obtaining a token pair
15489
- * @param logoutOnFailure Whether to log out if the request fails
15490
- * @returns An array containing two elements: 1) a Promise for the access and refresh tokens, and 2) the UUID of the
15491
- * request, so the request can be cancelled if necessary.
15492
- */
15493
- __publicField(this, "_getTokenPair", (credentials, logoutOnFailure = true) => {
15494
- const uuid = v4();
15495
- const responsePromise = this.enqueueRequest({
15496
- uuid,
15497
- description: "Get token pair",
15498
- method: HttpMethod.POST,
15499
- url: "/api/token/",
15500
- payload: credentials,
15501
- isAuthNeeded: false,
15502
- checkAuth: false,
15503
- blockers: [],
15504
- blocks: []
15505
- }).then(parseTokens).catch((e) => {
15506
- if (logoutOnFailure) {
15507
- void this.logout().then();
15508
- }
15509
- throw e;
15510
- });
15511
- return [responsePromise, uuid];
15512
- });
15513
15438
  /**
15514
15439
  * Takes refresh token and gets a new token pair
15515
15440
  * @async
@@ -15520,7 +15445,7 @@ class AuthService extends BaseApiService {
15520
15445
  const promise = this.enqueueRequest({
15521
15446
  description: "Get renewed tokens",
15522
15447
  method: HttpMethod.POST,
15523
- url: "/api/token/refresh/",
15448
+ url: this.refreshTokensUrl,
15524
15449
  payload: { refresh: refreshToken },
15525
15450
  isAuthNeeded: false,
15526
15451
  blockers: [],
@@ -15531,14 +15456,14 @@ class AuthService extends BaseApiService {
15531
15456
  immediate: true
15532
15457
  }).catch((e) => {
15533
15458
  console.error("Could not renew tokens; logging out due to error:", e);
15534
- void this.logout();
15459
+ void this.clearAuth();
15535
15460
  return void 0;
15536
15461
  });
15537
15462
  let response = void 0;
15538
15463
  try {
15539
15464
  response = await promise;
15540
15465
  } catch (e) {
15541
- await this.logout();
15466
+ await this.clearAuth();
15542
15467
  }
15543
15468
  if (!response)
15544
15469
  return void 0;
@@ -15549,40 +15474,12 @@ class AuthService extends BaseApiService {
15549
15474
  return { accessToken: response.access, refreshToken: response.refresh };
15550
15475
  });
15551
15476
  }
15552
- /**
15553
- * Attempts to log into Hemora using given credentials
15554
- * @param {string} username
15555
- * @param {string} password
15556
- */
15557
- async login(username, password) {
15558
- const [promise, uuid] = this._getTokenPair({ username, password }, false);
15559
- const initialDataUuid = v4();
15560
- const timeout = 5;
15561
- let timedOut = false;
15562
- const timeoutPromise = new Promise((_, reject) => {
15563
- setTimeout(() => {
15564
- timedOut = true;
15565
- this.dispatch(markForDeletion(uuid));
15566
- this.dispatch(markForDeletion(initialDataUuid));
15567
- reject(new APIError({ message: `Request timed out after ${timeout} seconds` }));
15568
- }, timeout * 1e3);
15569
- });
15570
- const successPromise = promise.then((tokens) => {
15571
- if (timedOut) {
15572
- return void 0;
15573
- }
15574
- this.dispatch(setTokens(tokens));
15575
- this.dispatch(setLoggedIn(true));
15576
- this.dispatch({ type: "rehydrated/setRehydrated", payload: true });
15577
- });
15578
- return Promise.race([timeoutPromise, successPromise]);
15579
- }
15580
15477
  /**
15581
15478
  * Logs the user out
15582
15479
  */
15583
- async logout() {
15480
+ async clearAuth() {
15584
15481
  this.dispatch(setLoggedIn(false));
15585
- this.dispatch(clearTokens());
15482
+ this.clearTokens();
15586
15483
  this.dispatch(setActiveProjectId(null));
15587
15484
  this.dispatch(setActiveWorkspaceId(null));
15588
15485
  this.dispatch({ type: RESET_STATE });
@@ -15594,7 +15491,7 @@ class AuthService extends BaseApiService {
15594
15491
  * Attempts to renew tokens
15595
15492
  */
15596
15493
  async renewTokens() {
15597
- const dyingRefreshToken = this._getRefreshToken();
15494
+ const dyingRefreshToken = this.getRefreshToken();
15598
15495
  if (!dyingRefreshToken) {
15599
15496
  throw new Error("No refresh token found");
15600
15497
  }
@@ -15603,48 +15500,20 @@ class AuthService extends BaseApiService {
15603
15500
  if (!tokens) {
15604
15501
  return void 0;
15605
15502
  }
15606
- const { accessToken, refreshToken } = tokens;
15607
15503
  console.log("Got renewed tokens");
15608
- this.dispatch(setTokens({ accessToken, refreshToken }));
15504
+ this.setTokens(tokens);
15609
15505
  } catch (e) {
15610
15506
  console.error("Could not renew tokens; logging out.");
15611
- await this.logout();
15507
+ await this.clearAuth();
15612
15508
  throw e;
15613
15509
  }
15614
15510
  }
15615
- /**
15616
- * Register a new user
15617
- */
15618
- register(payload) {
15619
- return this.enqueueRequest({
15620
- description: "Register",
15621
- method: HttpMethod.POST,
15622
- url: "/authentication/users/register/",
15623
- isAuthNeeded: false,
15624
- payload,
15625
- blockers: [],
15626
- blocks: []
15627
- });
15628
- }
15629
- async resetPassword(email) {
15630
- return this.enqueueRequest({
15631
- description: "Reset password",
15632
- method: HttpMethod.PATCH,
15633
- url: "/authentication/users/reset-password/",
15634
- isAuthNeeded: false,
15635
- payload: {
15636
- email
15637
- },
15638
- blockers: [],
15639
- blocks: []
15640
- });
15641
- }
15642
15511
  /**
15643
15512
  * Checks whether the tokens will be expiring soon
15644
15513
  * @returns {boolean}
15645
15514
  */
15646
15515
  tokenIsExpiringSoon() {
15647
- const accessToken = this._getAccessToken();
15516
+ const accessToken = this.getAccessToken();
15648
15517
  if (!accessToken) {
15649
15518
  return false;
15650
15519
  }
@@ -15658,53 +15527,99 @@ class AuthService extends BaseApiService {
15658
15527
  const secondsUntilExpiry = expiryDate - currentDate;
15659
15528
  return secondsUntilExpiry < EXPIRING_SOON_THRESHOLD;
15660
15529
  }
15661
- async replaceProfilePicture(file) {
15662
- const hash = await hashFile(file);
15663
- await this.client.files.addCache(file, hash);
15664
- const [fileProps] = await this.client.files.uploadFileToS3(hash);
15665
- this.dispatch(setProfilePicture({ file: `/files/${fileProps.file}`, file_sha1: hash }));
15666
- return this.enqueueRequest({
15667
- description: "Replace profile picture",
15668
- method: HttpMethod.PATCH,
15669
- url: "/authentication/users/profile-details/",
15670
- payload: fileProps,
15671
- blockers: [],
15672
- blocks: []
15673
- });
15530
+ getAuthHeader() {
15531
+ const accesseToken = selectAccessToken(this.client.store.getState());
15532
+ return `Bearer ${accesseToken}`;
15674
15533
  }
15675
- async addFavouriteProjectId(projectId) {
15676
- this.dispatch(addFavouriteProjectId(projectId));
15677
- return this.enqueueRequest({
15678
- description: "Add favourite project",
15679
- method: HttpMethod.POST,
15680
- url: `/authentication/users/favourite-project/${projectId}/`,
15681
- blockers: [],
15682
- blocks: []
15683
- });
15534
+ async prepareAuth() {
15535
+ if (!this.tokenIsExpiringSoon())
15536
+ return;
15537
+ try {
15538
+ await this.renewTokens();
15539
+ } catch (e) {
15540
+ if (e instanceof APIError) {
15541
+ await this.clearAuth();
15542
+ }
15543
+ return Promise.reject(e);
15544
+ }
15684
15545
  }
15685
- async removeFavouriteProjectId(projectId) {
15686
- this.dispatch(removeFavouriteProjectId(projectId));
15687
- return this.enqueueRequest({
15688
- description: "Add favourite project",
15689
- method: HttpMethod.POST,
15690
- url: `/authentication/users/unfavourite-project/${projectId}/`,
15691
- blockers: [`favorite-project-${projectId}`],
15692
- blocks: [`favorite-project-${projectId}`]
15693
- });
15546
+ /* if not successfull in gracefully handling an unauthorized response, throw and APIError */
15547
+ async handleUnauthorized(request2, response) {
15548
+ const state = this.client.store.getState();
15549
+ if (request2.url.endsWith("/token/refresh/")) {
15550
+ if (state.authReducer.isLoggedIn) {
15551
+ console.warn("No signed-in user to sign out.");
15552
+ }
15553
+ await this.clearAuth();
15554
+ throw new APIError({
15555
+ message: "You have been signed out due to inactivity.",
15556
+ response,
15557
+ discard: true
15558
+ });
15559
+ }
15560
+ if (state.authReducer.isLoggedIn) {
15561
+ await this.renewTokens();
15562
+ } else {
15563
+ console.debug("Forbidden; user is not logged in.");
15564
+ throw new APIError({
15565
+ message: "Incorrect username or password.",
15566
+ response,
15567
+ discard: true
15568
+ });
15569
+ }
15694
15570
  }
15695
- async joinApplication(projectInviteId, verification_code, username, password) {
15696
- return this.enqueueRequest({
15697
- description: "Join application",
15698
- method: HttpMethod.PATCH,
15699
- url: `/authentication/join-app/${projectInviteId}/${verification_code}/`,
15700
- payload: {
15701
- username,
15702
- password
15703
- },
15571
+ /**
15572
+ * Attempts to log into Hemora using given credentials
15573
+ * @param {string} username
15574
+ * @param {string} password
15575
+ */
15576
+ async initAuth(username, password) {
15577
+ const uuid = v4();
15578
+ const promise = this.enqueueRequest({
15579
+ uuid,
15580
+ description: "Get token pair",
15581
+ method: HttpMethod.POST,
15582
+ url: this.initTokensUrl,
15583
+ payload: { username, password },
15704
15584
  isAuthNeeded: false,
15585
+ checkAuth: false,
15705
15586
  blockers: [],
15706
15587
  blocks: []
15588
+ }).then(parseTokens);
15589
+ const timeout = 5;
15590
+ let timedOut = false;
15591
+ const timeoutPromise = new Promise((_, reject) => {
15592
+ setTimeout(() => {
15593
+ timedOut = true;
15594
+ this.dispatch(markForDeletion(uuid));
15595
+ reject(new APIError({ message: `Request timed out after ${timeout} seconds` }));
15596
+ }, timeout * 1e3);
15597
+ });
15598
+ const successPromise = promise.then((tokens) => {
15599
+ if (timedOut) {
15600
+ return void 0;
15601
+ }
15602
+ this.setTokens(tokens);
15603
+ this.dispatch(setLoggedIn(true));
15604
+ this.dispatch({ type: "rehydrated/setRehydrated", payload: true });
15707
15605
  });
15606
+ return Promise.race([timeoutPromise, successPromise]);
15607
+ }
15608
+ }
15609
+ const CLASS_NAME_TO_SERVICE = {};
15610
+ class BaseApiService {
15611
+ constructor(sdk, auth) {
15612
+ __publicField(this, "client");
15613
+ __publicField(this, "auth");
15614
+ CLASS_NAME_TO_SERVICE[this.constructor.name] = this;
15615
+ this.client = sdk;
15616
+ this.auth = auth;
15617
+ }
15618
+ async enqueueRequest(requestDetails) {
15619
+ return this.client.enqueueRequest(requestDetails, this.host, this.constructor.name);
15620
+ }
15621
+ dispatch(action) {
15622
+ this.client.store.dispatch(action);
15708
15623
  }
15709
15624
  }
15710
15625
  class CategoryService extends BaseApiService {
@@ -18105,18 +18020,17 @@ class FileService extends BaseApiService {
18105
18020
  }
18106
18021
  class EmailVerificationService extends BaseApiService {
18107
18022
  async getVerificationCode(verificationCode) {
18108
- const requestDetails = {
18023
+ return this.enqueueRequest({
18109
18024
  description: "Get verification code",
18110
18025
  method: HttpMethod.GET,
18111
18026
  url: `/verification/email-verification/${verificationCode}/`,
18112
18027
  isAuthNeeded: false,
18113
18028
  blockers: [],
18114
18029
  blocks: []
18115
- };
18116
- return this.enqueueRequest(requestDetails);
18030
+ });
18117
18031
  }
18118
18032
  validateVerificationCode(verificationCode, payload = void 0) {
18119
- const requestDetails = {
18033
+ return this.enqueueRequest({
18120
18034
  description: "Validate verification code",
18121
18035
  method: HttpMethod.POST,
18122
18036
  url: `/verification/email-verification/${verificationCode}/`,
@@ -18124,8 +18038,7 @@ class EmailVerificationService extends BaseApiService {
18124
18038
  payload,
18125
18039
  blockers: [],
18126
18040
  blocks: []
18127
- };
18128
- return this.enqueueRequest(requestDetails);
18041
+ });
18129
18042
  }
18130
18043
  }
18131
18044
  class EmailDomainsService extends BaseApiService {
@@ -18770,13 +18683,13 @@ export {
18770
18683
  AssetTypeAttachmentService,
18771
18684
  AssetTypeService,
18772
18685
  AttachmentModel,
18773
- AuthService,
18774
18686
  BaseApiService,
18775
18687
  BaseField,
18776
18688
  BaseFormElement,
18777
18689
  BaseSDK,
18778
18690
  BooleanField,
18779
18691
  BooleanInput,
18692
+ CLASS_NAME_TO_SERVICE,
18780
18693
  CategoryService,
18781
18694
  ColorPicker,
18782
18695
  Colors,
@@ -18814,6 +18727,7 @@ export {
18814
18727
  IssueTypeService,
18815
18728
  IssueUpdateChange,
18816
18729
  IssueUpdateService,
18730
+ JWTService,
18817
18731
  LicenseLevel,
18818
18732
  LicenseService,
18819
18733
  LicenseStatus,
@@ -19410,6 +19324,8 @@ export {
19410
19324
  userSlice,
19411
19325
  validateForm,
19412
19326
  valueIsFile,
19327
+ versioningReducer,
19328
+ versioningSlice,
19413
19329
  warningColor,
19414
19330
  workspaceReducer,
19415
19331
  workspaceSlice,