Skip to content

Added optional getSize func to Storage. #450

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export default class Storage<T> {
}

async getSize(): Promise<number | null> {
if (this.storage.getSize) {
return this.storage.getSize(this.key);
}

const data = await this.storage.getItem(this.key);

if (data == null) {
Expand Down
18 changes: 13 additions & 5 deletions src/__mocks__/MockStorage.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
import { PersistentStorage } from '../types';

export default class MockStorage implements PersistentStorage {
storage: Map<string, string>;
export default class MockStorage implements PersistentStorage<any> {
storage: Map<string, any>;

constructor() {
this.storage = new Map();
}

setItem(key: string, data: string): Promise<any> {
setItem(key: string, data: any): Promise<void> {
return new Promise(resolve => {
this.storage.set(key, data);
resolve();
});
}

removeItem(key: string): Promise<any> {
removeItem(key: string): Promise<void> {
return new Promise((resolve, reject) => {
this.storage.delete(key);
resolve();
});
}

getItem(key: string): Promise<string> {
getItem(key: string): Promise<any> {
return new Promise((resolve, reject) => {
resolve(this.storage.get(key));
});
}

getSize(key: string): Promise<number> {
return new Promise(resolve => {
if (this.storage.size === 0) return resolve(0);
const data = this.storage.get(key);
resolve((typeof data === 'string' ? data : JSON.stringify(data)).length);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Worried about performance implication of serialization just for size limitation.
Typically when using non strings clients data can be very large (like in index db)
I would be reluctant to merge it.

Maybe something like Object.keys(data).length could work?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that would require another sizeFunction argument to be passed by user then default one would simply use string length function

});
}
}
22 changes: 21 additions & 1 deletion src/__tests__/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Storage from '../Storage';
describe('Storage', () => {
const storage = new Storage({
storage: new MockStorage(),
});
} as any);

it('writes, reads, & deletes data from persistent storage', async () => {
await expect(storage.write('yo yo yo')).resolves.toBe(undefined);
Expand All @@ -30,4 +30,24 @@ describe('Storage', () => {
await expect(storage.read()).resolves.toBe('yo yo yo');
})
})

describe('when it has a getSize function', () => {
it('calculates its own size', async () => {
storage.purge();
await expect(storage.getSize()).resolves.toBe(0);
await expect(storage.write('yo yo yo')).resolves.toBe(undefined);
await expect(storage.getSize()).resolves.toBeGreaterThan(0);
})
})

describe('when it does not have a getSize function', () => {
it('calculates based on string size if applicable', async () => {
const originalGetSize = storage.storage.getSize;
const content = 'yo yo yo yo';
storage.storage.getSize = undefined;
await expect(storage.write(content)).resolves.toBe(undefined);
await expect(storage.getSize()).resolves.toBe(content.length);
storage.storage.getSize = originalGetSize;
})
})
});
10 changes: 10 additions & 0 deletions src/storageWrappers/LocalStorageWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class LocalStorageWrapper implements PersistentStorage<string | null> {
return this.removeItem(key);
}
}

getSize(): number {
return new Blob(Object.values(this.storage)).size;
}
}

interface LocalStorageInterface {
Expand All @@ -31,3 +35,9 @@ interface LocalStorageInterface {
setItem(key: string, value: string): void;
removeItem(key: string): void;
}

declare class Blob {
// Actual type defintion: https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts#L2418
constructor(data: any);
readonly size: number;
}
10 changes: 10 additions & 0 deletions src/storageWrappers/SessionStorageWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export class SessionStorageWrapper implements PersistentStorage<string | null> {
return this.removeItem(key);
}
}

getSize(): number {
return new Blob(Object.values(this.storage)).size;
}
}

interface SessionStorageInterface {
Expand All @@ -31,3 +35,9 @@ interface SessionStorageInterface {
setItem(key: string, value: string): void;
removeItem(key: string): void;
}

declare class Blob {
// Actual type defintion: https://github.com/microsoft/TypeScript/blob/main/lib/lib.dom.d.ts#L2418
constructor(data: any);
readonly size: number;
}
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface PersistentStorage<T> {
getItem: (key: string) => Promise<T | null> | T | null;
setItem: (key: string, value: T) => Promise<T> | Promise<void> | void | T;
removeItem: (key: string) => Promise<T> | Promise<void> | void;
getSize?(key: string): Promise<number> | number | null;
}

type StorageType<T, TSerialize extends boolean> = TSerialize extends true
Expand Down