Home GitMarker - Data Storage and Secrets Storage
Post
Cancel

GitMarker - Data Storage and Secrets Storage


At a certain point in the project, I had the main functionalities working as I expected, but one crucial aspect was missing yet: state persistence. There was no point in making a bookmark manager if I couldn’t persist smoothly in what the user does. VS Code provides good built-in options for it, and I’ll focus on what I’ve used for persisting data and sensible information into GitMarker.

Giving you some Context


I had to look at the Data Storage options the extension API could provide and pick the best fit for storing the bookmarks the way I wanted. Notice that at the moment the extension is activated, the entry point is the async activate() method, located on extension.ts I showed you before. This method carries the context argument of type vscode.ExtensionContext.

For convenience, I simply created a Singleton service I named ContextManager to make the context easily available everywhere and, instantiated it in the activate method of the extension.js. There are probably other ways to do it, but I just kept it globally accessible the way it was without passing it as an argument to commands or services.

ContextManager.init(context);

In case you want to check another example of Singleton, I have a project where I keep a collection of useful design patterns implemented in C#.

Save the Memento


Once the context is available and loaded, we can choose between the 4 Data Storage options just by calling them from it, and it returns a Memento object, which represents a storage utility able to store and retrieve values.

I thought that the best fit for my simple needs would be using the ExtensionContext.globalState, just because I wanted it available during and between VSCode Sessions, independently of workspaces. Also, the data I store is nothing more than a serialized typescript object.

DataStorageManager

I knew I would have to use this mechanism from many places in the application. So I thought it would be better to have a service for abstracting the operations with Memento; hence creating DataStorageManager was evident in the sense I could @inject it everywhere and also ensures it would continuously operate over the globalState.

getValue

Expects a Key of string and return a T (generic) object.

setValue

Expects a Key of string and a T (generic) object and store it.

clearValues

Expects a Key of string and sets undefined to a stored object

The usage is pretty simple, but I highly recommend using exported CONSTANTS as Keys to ensure maintenance and readability.

const storedCategories = this.localStorageManager
  .getValue<Category[]>(FAVORITE_REPOS_KEY);

I have a Secret


As you probably noted at this point, the extension is just a Bookmark manager that makes usage of the GitHub Rest API for searching repositories by the terms you want to search and other things. This API is public, and the usage is free, although the request rate limit may vary according to their plans and if you’re authenticated or not. In few words, to have the best experience, you may need to provide a Personal Access Token (PAT).

I had the challenge to ensure the stored PAT is safely stored and never be available to other extensions. It would be a security breach that a malicious extension could exploit, and I couldn’t allow that.

PersonalAccessTokenManager

Luckily, the ExtensionContext started to provide access to the SecretStorage after VS Code v1.53.0. This mechanism is the perfect solution to store confidential information, such as tokens or passwords, and then I came up with the PersonalAccessTokenManager service to abstract it, similar to what I’ve done with the DataStorage.


Note:: for Windows users, you can check or revoke this credential from the credential manager.
On GitMarker, there’s a ❌ Clear PAT menu option on GitMarker that cleans that key as well.





This post is licensed under CC BY 4.0 by the author.