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

Add State Provider Framework #6640

Merged
merged 54 commits into from
Nov 9, 2023
Merged

Conversation

justindbaur
Copy link
Member

@justindbaur justindbaur commented Oct 19, 2023

Type of change

- [ ] Bug fix
- [x] New feature development
- [x] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other

Objective

PM-4342 PM-4343

This adds a framework for how a domain can own their own state and gain observable access to the state that you own, both globally and for a user. There is no implementation of this framework in this PR, a PR with an implementation will follow the merging of this.

Code changes

StateDefinition

This is an object that will be created in a common file that is owned by platform and all creations of this type outside of there is strictly prohibited. It encapsulates the rule of the name for your state (most commonly a domain ex. FolderService, CipherService.) and where this state should be stored. The name and location combination of this state should be unique across all other uses of StateDefinitions yet should remain human readable (not a uuid). This is why creations of this type will be required to be in a single file owned by the platform team, and tests will be created to help ensure this.

KeyDefinition

This is a type that builds upon the concept of StateDefinition (in fact the creation of a KeyDefinition requires a `StateDefinition) by sub-dividing a state into smaller chunks of information that a state or domain will care about. The creation of this object is also limited to a single file that is owned by platform. The keys within a state must also remain unique. This type also ensures that you give it logic to convert the JSON form of your data into the data you expect to actually be there. This is because state is often round tripped through it's json form and it is not able to be converted back unless specific logic is provided.

This type also contains several helper methods for creating your keys in popular configurations like arrays or records.

GlobalState

This class provides you with 2 API's. One is an update method that takes a callback for manipulating your state. The second API is a state$ observable that you may subscribe to that will provide you with emissions any time your state is updated. This may be from your own usage of the update method or this may be from the usage of the update method in a different context than you.

GlobalStateProvider

A simple class that gets your GlobalState implementation for the given KeyDefinition.

UserState

This is a similar concept to GlobalState but for state about scoped to users. It has a similar update method for updating the state of the currently active user and an observable for subscribing to the data from the current user. It also gives you the ability to get your state directly from it's storage and the ability to update state for a specified user via their id.

Screenshots

Before you submit

  • Please add unit tests where it makes sense to do so (encouraged but not required)
  • If this change requires a documentation update - notify the documentation team
  • If this change has particular deployment requirements - notify the DevOps team
  • Ensure that all UI additions follow WCAG AA requirements

justindbaur and others added 11 commits October 19, 2023 15:46
Add a class for encapsulation information about state
this will often be for a domain but creations of this will
exist outside of a specific domain, hence just the name State.
This adds a type that extends state definition into another sub-key
and forces creators to define the data that will be stored and how
to read the data that they expect to be stored.
Adds to function to help building keys for both keys scoped
to a specific user and for keys scoped to global storage.

Co-authored-by: Matt Gibson <[email protected]>
Original commit by Matt: 823d954
Co-authored-by: Matt Gibson <[email protected]>
Create a helper that creats an Observable from a chrome event
and removes the listener when the subscription is completed.
Use fromChromeEvent to create an observable from chrome
event and map that into our expected shape.
Co-authored-by: Matt Gibson <[email protected]>
@github-actions github-actions bot added the needs-qa Marks a PR as requiring QA approval label Oct 19, 2023
@bitwarden-bot
Copy link

bitwarden-bot commented Oct 19, 2023

Logo
Checkmarx One – Scan Summary & Detailsa7897a6e-0a4a-4696-ac12-eb5bf65cb1fd

No New Or Fixed Issues Found

@MGibson1 MGibson1 self-assigned this Oct 19, 2023
Copy link
Member

@MGibson1 MGibson1 left a comment

Choose a reason for hiding this comment

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

Early review just because I think you'll the very first comment I had 😄

apps/browser/src/platform/browser/from-chrome-event.ts Outdated Show resolved Hide resolved
apps/browser/src/platform/browser/from-chrome-event.ts Outdated Show resolved Hide resolved
Rework fromChromeEvent so we have to lie to TS less and
remove unneeded generics. I did this by caring less about
the function and more about the parameters only.

Co-authored-by: Matt Gibson <[email protected]>
Copy link
Member

@MGibson1 MGibson1 left a comment

Choose a reason for hiding this comment

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

I think there's a bit more polish to do before this is ready

libs/common/src/platform/abstractions/storage.service.ts Outdated Show resolved Hide resolved
libs/common/src/platform/misc/key-builders.ts Outdated Show resolved Hide resolved
libs/common/src/platform/misc/key-builders.ts Outdated Show resolved Hide resolved
libs/common/src/platform/state/derived-state-definition.ts Outdated Show resolved Hide resolved
Comment on lines 97 to 98
const accountChangeSubscription = activeAccountData$.subscribe();
const storageUpdateSubscription = storageUpdates$.subscribe((data) => {
Copy link
Member

Choose a reason for hiding this comment

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

💭 Looking at this again, it seems fragile to have two separate watchers be responsible for the current value of a single observable. I might be being paranoid here, though

libs/common/src/platform/misc/key-builders.ts Outdated Show resolved Hide resolved
libs/common/src/platform/misc/key-builders.ts Outdated Show resolved Hide resolved
@justindbaur justindbaur requested a review from a team as a code owner November 3, 2023 18:49
MGibson1
MGibson1 previously approved these changes Nov 7, 2023
MGibson1
MGibson1 previously approved these changes Nov 7, 2023
Copy link
Member

@shane-melton shane-melton left a comment

Choose a reason for hiding this comment

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

Looks good for Vault! Thanks for creating the ticket!

@justindbaur justindbaur removed the needs-qa Marks a PR as requiring QA approval label Nov 9, 2023
@justindbaur justindbaur merged commit e1b5b83 into master Nov 9, 2023
59 of 60 checks passed
@justindbaur justindbaur deleted the add-state-provider-framework branch November 9, 2023 22:06
BlackDex pushed a commit to BlackDex/bitwarden-clients that referenced this pull request Nov 21, 2023
* Add StateDefinition

Add a class for encapsulation information about state
this will often be for a domain but creations of this will
exist outside of a specific domain, hence just the name State.

* Add KeyDefinition

This adds a type that extends state definition into another sub-key
and forces creators to define the data that will be stored and how
to read the data that they expect to be stored.

* Add key-builders helper functions

Adds to function to help building keys for both keys scoped
to a specific user and for keys scoped to global storage.

Co-authored-by: Matt Gibson <[email protected]>

* Add updates$ stream to existing storageServices

Original commit by Matt: 823d954
Co-authored-by: Matt Gibson <[email protected]>

* Add fromChromeEvent helper

Create a helper that creats an Observable from a chrome event
and removes the listener when the subscription is completed.

* Implement `updates$` property for chrome storage

Use fromChromeEvent to create an observable from chrome
event and map that into our expected shape.

* Add GlobalState Abstractions

* Add UserState Abstractions

* Add Default Implementations of User/Global state

Co-authored-by: Matt Gibson <[email protected]>

* Add Barrel File for state

Co-authored-by: Matt Gibson <[email protected]>

* Fix ChromeStorageServices

* Rework fromChromeEvent

Rework fromChromeEvent so we have to lie to TS less and
remove unneeded generics. I did this by caring less about
the function and more about the parameters only.

Co-authored-by: Matt Gibson <[email protected]>

* Fix UserStateProvider Test

* Add Inner Mock & Assert Calls

* Update Tests to use new keys

Use different key format

* Prefer returns over mutations in update

* Update Tests

* Address PR Feedback

* Be stricter with userId parameter

* Add Better Way To Determine if it was a remove

* Fix Web & Browser Storage Services

* Fix Desktop & CLI Storage Services

* Fix Test Storage Service

* Use createKey Helper

* Prefer implement to extending

* Determine storage location in providers

* Export default providers publicly

* Fix user state tests

* Name tests

* Fix CLI

* Prefer Implement In Chrome Storage

* Remove Secure Storage Option

Also throw an exception for subscribes to the secure storage observable.

* Update apps/browser/src/platform/browser/from-chrome-event.ts

Co-authored-by: Oscar Hinton <[email protected]>

* Enforce state module barrel file

* Fix Linting Error

* Allow state module import from other modules

* Globally Unregister fromChromeEvent Listeners

Changed fromChromeEvent to add its listeners through the BrowserApi, so that
they will be unregistered when safari closes.

* Test default global state

* Use Proper Casing in Parameter

* Address Feedback

* Update libs/common/src/platform/state/key-definition.ts

Co-authored-by: Oscar Hinton <[email protected]>

* Add `buildCacheKey` Method

* Fix lint errors

* Add Comment

Co-authored-by: Oscar Hinton <[email protected]>

* Use Generic in callback parameter

* Refactor Out DerivedStateDefinition

* Persist Listener Return Type

* Add Ticket Link

---------

Co-authored-by: Matt Gibson <[email protected]>
Co-authored-by: Matt Gibson <[email protected]>
Co-authored-by: Oscar Hinton <[email protected]>
quexten pushed a commit to quexten/clients that referenced this pull request Feb 10, 2024
* Add StateDefinition

Add a class for encapsulation information about state
this will often be for a domain but creations of this will
exist outside of a specific domain, hence just the name State.

* Add KeyDefinition

This adds a type that extends state definition into another sub-key
and forces creators to define the data that will be stored and how
to read the data that they expect to be stored.

* Add key-builders helper functions

Adds to function to help building keys for both keys scoped
to a specific user and for keys scoped to global storage.

Co-authored-by: Matt Gibson <[email protected]>

* Add updates$ stream to existing storageServices

Original commit by Matt: 823d954
Co-authored-by: Matt Gibson <[email protected]>

* Add fromChromeEvent helper

Create a helper that creats an Observable from a chrome event
and removes the listener when the subscription is completed.

* Implement `updates$` property for chrome storage

Use fromChromeEvent to create an observable from chrome
event and map that into our expected shape.

* Add GlobalState Abstractions

* Add UserState Abstractions

* Add Default Implementations of User/Global state

Co-authored-by: Matt Gibson <[email protected]>

* Add Barrel File for state

Co-authored-by: Matt Gibson <[email protected]>

* Fix ChromeStorageServices

* Rework fromChromeEvent

Rework fromChromeEvent so we have to lie to TS less and
remove unneeded generics. I did this by caring less about
the function and more about the parameters only.

Co-authored-by: Matt Gibson <[email protected]>

* Fix UserStateProvider Test

* Add Inner Mock & Assert Calls

* Update Tests to use new keys

Use different key format

* Prefer returns over mutations in update

* Update Tests

* Address PR Feedback

* Be stricter with userId parameter

* Add Better Way To Determine if it was a remove

* Fix Web & Browser Storage Services

* Fix Desktop & CLI Storage Services

* Fix Test Storage Service

* Use createKey Helper

* Prefer implement to extending

* Determine storage location in providers

* Export default providers publicly

* Fix user state tests

* Name tests

* Fix CLI

* Prefer Implement In Chrome Storage

* Remove Secure Storage Option

Also throw an exception for subscribes to the secure storage observable.

* Update apps/browser/src/platform/browser/from-chrome-event.ts

Co-authored-by: Oscar Hinton <[email protected]>

* Enforce state module barrel file

* Fix Linting Error

* Allow state module import from other modules

* Globally Unregister fromChromeEvent Listeners

Changed fromChromeEvent to add its listeners through the BrowserApi, so that
they will be unregistered when safari closes.

* Test default global state

* Use Proper Casing in Parameter

* Address Feedback

* Update libs/common/src/platform/state/key-definition.ts

Co-authored-by: Oscar Hinton <[email protected]>

* Add `buildCacheKey` Method

* Fix lint errors

* Add Comment

Co-authored-by: Oscar Hinton <[email protected]>

* Use Generic in callback parameter

* Refactor Out DerivedStateDefinition

* Persist Listener Return Type

* Add Ticket Link

---------

Co-authored-by: Matt Gibson <[email protected]>
Co-authored-by: Matt Gibson <[email protected]>
Co-authored-by: Oscar Hinton <[email protected]>
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.

6 participants