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

Operation repo refactor #1179

Merged
merged 15 commits into from
Sep 30, 2024
Merged

Operation repo refactor #1179

merged 15 commits into from
Sep 30, 2024

Conversation

shepherd-l
Copy link
Contributor

@shepherd-l shepherd-l commented Jun 6, 2024

Description

1 Line Summary

An operation repo refactor that will help with the implementation of identity verification

Details

  • Adds a delta queue to the operation repo to flush all executor deltas at the same time. Now the operation repo will be in charge of enqueuing deltas and passing the delta to its correct executor. Previously, each executor was in charge of enqueuing and flushing their own delta queue.
    • Makes it easier to implement pausing on the operation repo for Jwt when receiving an invalid token.
    • This change also aligns the Web SDK more with the iOS SDK's operation repo implementation.
  • Groups push, sms, and email subscriptions under 1 executor. Previously push, sms, and email each had their own subscription executor.
    • Omits creating multiple subscription executors. Unnecessary now that there is only 1 flush from the operation repo that flushes all executors
    • Deprecates the pushSubscription, smsSubscription, and emailSubscription model names for 1 new name: subscriptions. This change requires a database upgrade.
  • Adds external id property to operations
    • Will be used to retry operations for users with a valid Jwt token. Accessed through Operation.model.externalId
  • Added to following tests below:

Unit Tests:

  • v5 to v6 database migration for subscription records. If a user was already logged in v5, the external id is added to each subscription record in v6.
  • Login test, where the external id is updated correctly for each model which will be used in deltas/operations.
    • Future work: add logout tests

Systems Affected

  • WebSDK
  • Backend
  • Dashboard

Validation

Tests

Info

Checklist

  • All the automated tests pass or I explained why that is not possible
  • I have personally tested this on my machine or explained why that is not possible
  • I have included test coverage for these changes or explained why they are not needed

Programming Checklist
Interfaces:

  • Don't use default export
  • New interfaces are in model files

Functions:

  • Don't use default export
  • All function signatures have return types
  • Helpers should not access any data but rather be given the data to operate on.

Typescript:

  • No Typescript warnings
  • Avoid silencing null/undefined warnings with the exclamation point

Other:

  • Iteration: refrain from using elem of array syntax. Prefer forEach or use map
  • Avoid using global OneSignal accessor for context if possible. Instead, we can pass it to function/constructor so that we don't call OneSignal.context

Screenshots

Info

Checklist

  • I have included screenshots/recordings of the intended results or explained why they are not needed

Related Tickets



This change is Reviewable

@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 2 times, most recently from 5424208 to a9bc2dc Compare July 11, 2024 03:02
@shepherd-l shepherd-l requested a review from jkasten2 July 11, 2024 18:32
@shepherd-l shepherd-l changed the title WIP: [Fix] Operation repo refactor [Fix] Operation repo refactor Jul 12, 2024
@nan-li nan-li self-requested a review July 12, 2024 19:58
src/core/operationRepo/OperationRepo.ts Outdated Show resolved Hide resolved
src/core/executors/SubscriptionExecutor.ts Outdated Show resolved Hide resolved
Object.values(this.store).forEach((executor) => {
if (
executor instanceof SubscriptionExecutor &&
Copy link
Member

@jkasten2 jkasten2 Jul 15, 2024

Choose a reason for hiding this comment

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

Does the ExecutorFactory already ensure we don't have duplicate executors? Or if it doesn't take care of it, shouldn't we just omit adding it in the first place?

Copy link
Contributor

Choose a reason for hiding this comment

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

Under this PR, the factory will return the reference to the single static subscription executor three times and be mapped to via the three ModelName keys that correspond to it: pushSubscriptions, emailSubscriptions, and smsSubscriptions

There's probably a cleaner way to do this without hard-coding a check here

Copy link
Contributor Author

@shepherd-l shepherd-l Jul 15, 2024

Choose a reason for hiding this comment

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

Adding on to what rgomezp said:

The ExecutorStore creates an executor for each value in ModelName
And there are 3 names for subscriptions:

  1. Identity
  2. Properties
  3. PushSubscriptions
  4. EmailSubscriptions
  5. SmsSubscriptions

This PR should ensure that there are no duplicate executors from the ExecutorFactory. But we could omit adding it in the first place by having 1 ModelName for the subscription executor instead. I don't think this would heavy lift but I'm not sure.

Copy link
Contributor

Choose a reason for hiding this comment

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

The ModelName enum is widely used, not just for the executor factory.

You would have to change the way the ExecutorStore indexes the executors to only have 3 keys. But this adds a point of maintenance in the future should we add new supported models.

Is there an issue arising from the differences in behavior between the WebSDK & iOS? Or is this PR aimed at bringing the SDKs to parity to address an outstanding issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having one flush that is controlled by the operation repo would make it easier to implement a JWT feature where we would like to pause the operation repo when we receive an invalid token. And with changing to 1 flush, I didn't think there would be a need to have 3 separate subscription executors for push, sms, email.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But this adds a point of maintenance in the future should we add new supported models.

Could you elaborate on what maintenance would be needed?

You would have to change the way the ExecutorStore indexes the executors to only have 3 keys.

I thought this change would consist of changing the enum and the previous references to use 1 subscription enum. What other things would need to be done?

Copy link
Contributor

@rgomezp rgomezp Jul 16, 2024

Choose a reason for hiding this comment

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

Could you elaborate on what maintenance would be needed?

The executor store store interface looks like

type ExecutorStoreInterface = {
  [key in ModelName]?: OSExecutor;
};

If you change it to say

type ExecutorStoreInterface = {
  identity: OSExecutor;
  properties: OSExecutor;
  subscriptions: OSExecutor;
};

or similar you would have to update in two places should you add new model types. Not a huge deal though.

I thought this change would consist of changing the enum and the previous references to use 1 subscription enum. What other things would need to be done?

The enum isn't just used for creating executors. It's used throughout the user-model SDK.

Copy link
Member

Choose a reason for hiding this comment

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

I believe it would instead be changing this code:

export enum ModelName {
  Identity = 'identity',
  Properties = 'properties',
  // TO DO: make singular
  PushSubscriptions = 'pushSubscriptions',
  EmailSubscriptions = 'emailSubscriptions',
  SmsSubscriptions = 'smsSubscriptions',
}

to this:

export enum ModelName {
  Identity = 'identity',
  Properties = 'properties',
  Subscriptions = 'subscriptions',
}

I think this will simply things and means if we add another subscription type we won't have to change this again. We will have to update code that depends on ModelName however, and migrate any existing cached operations to the new name though.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this will simply things and means if we add another subscription type we won't have to change this again. We will have to update code that depends on ModelName however, and migrate any existing cached operations to the new name though.

That's a big lift. We also rely on the model name to differentiate between subscription models, like in the CoreModuleDirector, OSModel, etc...

__test__/support/constants.ts Outdated Show resolved Hide resolved
__test__/unit/core/operationRepo.test.ts Outdated Show resolved Hide resolved
src/core/executors/ExecutorFactory.ts Outdated Show resolved Hide resolved
Object.values(this.store).forEach((executor) => {
if (
executor instanceof SubscriptionExecutor &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Under this PR, the factory will return the reference to the single static subscription executor three times and be mapped to via the three ModelName keys that correspond to it: pushSubscriptions, emailSubscriptions, and smsSubscriptions

There's probably a cleaner way to do this without hard-coding a check here

src/core/executors/SubscriptionExecutor.ts Outdated Show resolved Hide resolved
@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 3 times, most recently from f2ca9d4 to 65ed239 Compare August 1, 2024 22:04
@shepherd-l shepherd-l changed the title [Fix] Operation repo refactor Operation repo refactor Aug 13, 2024
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 2 times, most recently from 7d53a4a to 0f60a67 Compare September 11, 2024 21:50
@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 2 times, most recently from 6e61697 to 7a2bcf7 Compare September 12, 2024 15:12
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/core/executors/ExecutorBase.ts Show resolved Hide resolved
@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 2 times, most recently from 5c0d2b6 to 1f73e80 Compare September 17, 2024 15:36
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/shared/services/IndexedDb.ts Show resolved Hide resolved
@shepherd-l shepherd-l force-pushed the fix/operationRepoRefactor branch 4 times, most recently from de0cb83 to 9992570 Compare September 20, 2024 16:07
Copy link
Contributor

@rgomezp rgomezp left a comment

Choose a reason for hiding this comment

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

Just a couple comments. After this I think LGTM

src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
src/core/CoreModuleDirector.ts Outdated Show resolved Hide resolved
Added a delta queue to the operation repo to flush all executor deltas at the same time. The operation repo's delta queue enqueues all deltas and then will sort and pass the delta to its correct executor. Previously, each executor was in charge of enqueuing and flushing their own delta queue. This change also aligns the Web SDK more with the iOS SDK's operation repo implementation.
Change pushSubscription, smsSubscription, and emailSubscription to point to the same SubscriptionExecutor. This way the SubscriptionExecutor only fires once instead of 3 times.
Running tests consecutively that enqueue operations to the SubscriptionExecutor will fail due to the operationQueue length not matching the expected value. pushSubscription, smsSubscription, emailSubscription now point to the same SubscriptionExecutor and the operationQueue was not being cleared for the next test. The other tests don't fail but I also cleared the operation queue there too.
Will be used to retry operations for users with a valid Jwt token. Accessed through Operation.model.externalId
External id from previously logged in user was kept on anonymous user's push subscription model when logged out
Change delta queue time back to 1 second. Was creating misalignment bugs due to the time being the same
Refactors ModelName enum for SmsSubscription, EmailSubscription, PushSubscription to 1 name called Subscriptions. This omits adding multiple subscription executors for sms, email, push and only creates 1 executor because all subscriptions are grouped together.
Fixes failing executor tests that result in removing the check. Tests were changed to: await the async TestEnvironment initialize function, remove additional initialization of core module because it was already created in TestEnvironment initialization, fix timers for tests that check the delta queue to check after 1 flush.
These logs clutter the console due to being called every second/5 seconds
Create new type to make it clear what subscription channel is being filtered for in getSubscriptionOfTypeWithToken
For externalId on OSModel
@shepherd-l shepherd-l merged commit 3cdf8a1 into main Sep 30, 2024
4 checks passed
@shepherd-l shepherd-l deleted the fix/operationRepoRefactor branch September 30, 2024 18:00
@shepherd-l shepherd-l mentioned this pull request Sep 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants