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

feat(api): AsyncStorage interfaces #1016

Merged
merged 7 commits into from
Nov 22, 2023
Merged
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
1 change: 1 addition & 0 deletions .config/tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"strict": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"strictNullChecks": true,
"allowJs": true,
"checkJs": true,
"allowSyntheticDefaultImports": true,
Expand Down
30 changes: 15 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
test-name: [lint, ts]
workspace: [default-storage-backend, core]
workspace: [default-storage, api]
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down Expand Up @@ -44,11 +44,11 @@ jobs:
with:
gradle-version: wrapper
arguments: react-native-async-storage_async-storage:test
build-root-directory: packages/default-storage-backend/example/android
build-root-directory: packages/default-storage/example/android
- name: Build e2e binary
run: |
yarn build:e2e:android
working-directory: packages/default-storage-backend
working-directory: packages/default-storage

ios:
name: iOS
Expand All @@ -59,7 +59,7 @@ jobs:
- name: Cache /.ccache
uses: actions/cache@v3
with:
path: packages/default-storage-backend/.ccache
path: packages/default-storage/.ccache
key: ccache-ios-${{ hashFiles('yarn.lock') }}
restore-keys: ccache-ios-
- name: Set up Node.js
Expand All @@ -73,15 +73,15 @@ jobs:
- name: Bundle JS
run: |
yarn bundle:ios
working-directory: packages/default-storage-backend
working-directory: packages/default-storage
- name: Install Pods
run: |
RCT_NEW_ARCH_ENABLED=1 pod install
working-directory: packages/default-storage-backend/example/ios
working-directory: packages/default-storage/example/ios
- name: Build e2e binary
run: |
yarn build:e2e:ios
working-directory: packages/default-storage-backend
working-directory: packages/default-storage

macos:
name: macOS
Expand All @@ -92,7 +92,7 @@ jobs:
- name: Cache /.ccache
uses: actions/cache@v3
with:
path: packages/default-storage-backend/.ccache
path: packages/default-storage/.ccache
key: ccache-macos-${{ hashFiles('yarn.lock') }}
restore-keys: ccache-macos-
- name: Set up Node.js
Expand All @@ -106,20 +106,20 @@ jobs:
- name: Bundle JS
run: |
yarn bundle:macos
working-directory: packages/default-storage-backend
working-directory: packages/default-storage
- name: Install Pods
run: |
RCT_NEW_ARCH_ENABLED=1 pod install
working-directory: packages/default-storage-backend/example/macos
working-directory: packages/default-storage/example/macos
- name: Build
run: |
yarn build:e2e:macos
working-directory: packages/default-storage-backend
working-directory: packages/default-storage
- name: Test
if: false
run: |
yarn test:e2e:macos
working-directory: packages/default-storage-backend
working-directory: packages/default-storage

windows:
name: Windows
Expand All @@ -142,11 +142,11 @@ jobs:
- name: Install Windows test app
run: |
yarn install-windows-test-app -p example/windows
working-directory: packages/default-storage-backend
working-directory: packages/default-storage
- name: Build
run: |
yarn react-native run-windows --release --arch x64 --logging --no-packager --no-launch --no-deploy --msbuildprops "BundleEntryFile=index.ts" --no-telemetry
working-directory: packages/default-storage-backend
working-directory: packages/default-storage

release:
name: Release
Expand Down Expand Up @@ -178,4 +178,4 @@ jobs:
git config user.email ${{ secrets.GH_BOT_EMAIL }}
git config user.name ${{ secrets.GH_BOT_NAME }}
yarn semantic-release
working-directory: packages/default-storage-backend
working-directory: packages/default-storage
File renamed without changes.
17 changes: 17 additions & 0 deletions packages/api/example/ExampleExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { StorageExtension } from "../src";

export interface MyExampleExtension extends StorageExtension {
double: (num: number) => Promise<number>;

uppercase: (text: string) => string;

key: string;
}

export class ExampleExtension implements MyExampleExtension {
key = "my-example-storage";

double = async (num: number) => num * 2;

uppercase = (text: string): string => text.toUpperCase();
}
69 changes: 69 additions & 0 deletions packages/api/example/ExampleStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { AsyncStorage, StorageKeys, StorageModel } from "../src";
import { ExampleExtension, MyExampleExtension } from "./ExampleExtension";

type MyModel = StorageModel<{
age: number;
name: string;
likes: boolean[];
}>;

// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
class ExampleStorage implements AsyncStorage<MyModel, MyExampleExtension> {
private storage: MyModel = {
age: null,
name: null,
likes: null,
};

getItem = async <K extends keyof MyModel>(key: K): Promise<MyModel[K]> => {
return this.storage[key];
};

setItem = async <K extends StorageKeys<MyModel>>(
key: K,
value: MyModel[K]
): Promise<void> => {
this.storage[key] = value;
};

removeItem = async <K extends StorageKeys<MyModel>>(
key: K
): Promise<void> => {
this.storage[key] = null;
};

getMany = async <K extends StorageKeys<MyModel>>(
keys: K[]
): Promise<{ [k in K]: MyModel[k] }> => {
return keys.reduce((entries, key) => {
entries[key] = this.storage[key] ?? null;
return entries;
}, {} as { [k in K]: MyModel[k] });
};

setMany = async <K extends StorageKeys<MyModel>>(entries: {
[k in K]: MyModel[k];
}): Promise<void> => {
Object.entries(entries).forEach((entry) => {
const key = entry[0] as K;
this.storage[key] = entry[1] as MyModel[K];
});
};

removeMany = async <K extends StorageKeys<MyModel>>(
keys: K[]
): Promise<void> => {
keys.forEach((k) => {
this.storage[k] = null;
});
};

clear = async (): Promise<void> => {
this.storage.age = null;
this.storage.name = null;
this.storage.likes = null;
};

ext: MyExampleExtension = new ExampleExtension();
}
5 changes: 5 additions & 0 deletions packages/api/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
8 changes: 6 additions & 2 deletions packages/core/package.json → packages/api/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@react-native-async-storage/core",
"name": "@react-native-async-storage/api",
"version": "0.0.0",
"description": "Core API of Async Storage",
"source": "src/index.ts",
Expand All @@ -17,7 +17,8 @@
"prepack": "yarn build",
"build": "bob build",
"test:lint": "eslint src/**",
"test:ts": "tsc --noEmit"
"test:ts": "tsc --noEmit",
"test:jest": "jest"
},
"keywords": [
"react-native",
Expand All @@ -30,8 +31,11 @@
"author": "Krzysztof Borowy <contact@kborowy.com>",
"license": "MIT",
"devDependencies": {
"@types/jest": "29.5.4",
"eslint": "8.26.0",
"jest": "29.5.0",
"react-native-builder-bob": "0.20.0",
"ts-jest": "29.1.1",
"typescript": "4.9.5"
},
"react-native-builder-bob": {
Expand Down
74 changes: 74 additions & 0 deletions packages/api/src/AsyncStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { StorageKeys, StorageModel } from "./StorageModel";
import type { StorageExtension } from "./StorageExtension";

/**
* AsyncStorage Interface
* Provides methods for managing asynchronous storage operations.
* @typeParam S - type of the storage model.
* @typeParam E - type of the storage extension, or unknown, if no extension is provided.
*/
export interface AsyncStorage<
S extends StorageModel,
E extends StorageExtension | unknown = unknown
> {
/**
* Retrieves a single item from storage based on the provided key.
* @param key - The key to identify the item within the storage.
* @returns Promise resolving to the value associated with the key,
* or null if the key does not exist.
*/
getItem<K extends StorageKeys<S>>(key: K): Promise<S[K]>;

/**
* Sets the value of the specified item in the storage.
* @param key - The key under which the value should be stored.
* @param value - The value to be stored.
* @returns Promise that resolves when the operation is completed.
*/
setItem<K extends StorageKeys<S>>(key: K, value: S[K]): Promise<void>;

/**
* Removes the item from storage identified by the provided key.
* @param key - The key of the item to be removed.
* @returns Promise that resolves when the operation is completed.
*/
removeItem<K extends StorageKeys<S>>(key: K): Promise<void>;

/**
* Retrieves multiple items from storage based on the provided keys.
* @param keys - An array of keys to identify the items to be retrieved.
* @returns Promise resolving to an object with key-value pairs,
* where the values are associated with the keys,
* or null if a key does not exist.
*/
getMany<K extends StorageKeys<S>>(keys: K[]): Promise<{ [k in K]: S[k] }>;

/**
* Sets multiple items in the storage.
* @param entries - An object containing key-value pairs to be stored.
* @returns Promise that resolves when the operation is completed.
*/
setMany<K extends StorageKeys<S>>(entries: {
[k in K]: S[k];
}): Promise<void>;

/**
* Removes multiple items from storage based on the provided keys.
* @param keys - An array of keys identifying the items to be removed.
* @returns Promise that resolves when the operation is completed.
*/
removeMany<K extends StorageKeys<S>>(keys: K[]): Promise<void>;

/**
* Clears all the data from the storage.
* @returns Promise that resolves when the operation is completed.
*/
clear(): Promise<void>;

/**
* Represents the extension for providing additional functionality
* beyond the standard storage interface.
* See {@link StorageExtension} for more details.
*/
ext: E;
}
6 changes: 6 additions & 0 deletions packages/api/src/StorageExtension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* The StorageExtension type serves as a means to extend the functionalities of the
* core interface beyond its operations. It acts as a placeholder for implementing
* additional methods.
*/
export type StorageExtension = {};
13 changes: 13 additions & 0 deletions packages/api/src/StorageModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* A type used to define the structure and shape of the data to be stored.
*/
export type StorageModel<
T extends Record<string, unknown> | unknown = unknown
> = {
[K in keyof T]: NonNullable<T[K]> | null;
};

/**
* A utility type to extract key.
*/
export type StorageKeys<T> = keyof T;
3 changes: 3 additions & 0 deletions packages/api/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { AsyncStorage } from "./AsyncStorage";
export type { StorageExtension } from "./StorageExtension";
export type { StorageKeys, StorageModel } from "./StorageModel";
7 changes: 7 additions & 0 deletions packages/api/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../.config/tsconfig.base.json",
"include": ["./src/**/*", "./example/**/*"],
"compilerOptions": {
"types": ["jest"]
}
}
5 changes: 0 additions & 5 deletions packages/core/src/index.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/core/tsconfig.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ build/
.gradle
local.properties
*.iml
!android/gradlew*

# Visual Studio
#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if [[ "$CCACHE_DISABLE" != "1" ]]; then

CCACHE_HOME=$(dirname $(dirname $(which ccache)))/opt/ccache

export CCACHE_DIR="$(git rev-parse --show-toplevel)/packages/default-storage-backend/.ccache"
export CCACHE_DIR="$(git rev-parse --show-toplevel)/packages/default-storage/.ccache"

export CC="${CCACHE_HOME}/libexec/clang"
export CXX="${CCACHE_HOME}/libexec/clang++"
Expand Down
Loading