Skip to content

Commit

Permalink
feat: 🎸 mobx-firebase-database
Browse files Browse the repository at this point in the history
  • Loading branch information
rakannimer committed Oct 10, 2018
0 parents commit f63aba9
Show file tree
Hide file tree
Showing 15 changed files with 11,415 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
dist/
.cache
.rpt2_cache
build/
coverage/
.vscode
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
## Mobx Firebase Database

## The Problem

While Firebase's Realtime Database enables you to build almost anything. Manipulating realtime data in your app can lead to quite difficult code to read.

## This Solution

Enter MobX. MobX is a powerful state management library that works with all front-end frameworks.

mobx-firebase-database allows you to map your Firebase data to MobX observables and interact with it using MobX

## Install

```sh
yarn add mobx-firebase-database
# If you're using firebase web
yarn add firebase
# If you're using firebase-admin
yarn add firebase-admin
# If you're using react-native-firebase
yarn add react-native-firebase
```

## Peer Dependencies

mobx-firebase-database works with : [`firebase`](https://www.npmjs.com/package/firebase), [`firebase-admin`](https://www.npmjs.com/package/firebase) and [`react-native-firebase`](https://www.npmjs.com/package/react-native-firebase)

Depending on which one you're using you will need to have it installed.

## Usage

### Web

Before starting to code, you need to get your Firebase Credentials :

Follow the steps [here](https://firebase.google.com/docs/web/setup#add_firebase_to_your_app) to get your config.

```typescript
import getMobxFire from "mobx-firebase-database";
import firebase from "firebase/app";
import "firebase/database";

// Don't worry about calling it more than once.
const { toBox, toArray, toMap, getFirebaseRef, destroy } = getMobxFire({
config,
firebase
});

// toBox
const postId = `my_post_id`;
const postRef = getFirebaseRef({ path: `posts/${postId}` });
const { value, unsub, update, set } = toBox(postRef);

const post = value.get(); // always contains the latest value of posts/${postId}
await update({ last_seen_at: firebase.database.ServerValue.TIMESTAMP });
value.get(); // {...post, last_seen_at: number}
await set(null);
value.get(); // null

// toMap
const postId = `my_post_id`;
const postsRef = getFirebaseRef({ path: `posts`, orderByKeys: true });
const { value, unsub, update, set } = toMap(postRef);
const allPostsIds = value.keys();
const post = value.get(`${postId}`);
/* const posts = value.get(); // always contains the latest value of posts/${postId}
await update({ last_seen_at: firebase.database.ServerValue.TIMESTAMP });
value.get(); // {...post, last_seen_at: number}
await set(null);
value.get(); // null */
```

## API

### `observable.box`

#### Input :

getMobxFire requires as input an object with shape :

- config:

```typescript
{
apiKey: string,
authDomain: string,
databaseURL: string,
storageBucket: string
}
```

- firebase : Firebase web client from `firebase` package

#### Output :

Object with shape :

```typescript
type Output<T> = {
value: IObservableValue<T>;
unsub: () => void;
update: (value: any) => Promise<void>;
set: (value: any) => Promise<void>;
};
```
60 changes: 60 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "mobx-firebase-database",
"version": "0.1.0",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"types": "dist/index.d.ts",
"scripts": {
"start": "npm run test:i",
"build": "rm -rf ./dist/ && rollup -c",
"test:i": "jest --watch --coverage",
"test": "jest --coverage",
"prepublish": "npm run test && npm run build",
"commit": "git-cz",
"setup:semantic-release": "npx semantic-release-cli setup"
},
"husky": {
"pre-commit": "prettier src/* --write",
"pre-push": "npm run test"
},
"repository": {
"type": "git",
"url": "https://github.com/rakannimer/mobx-firebase-database"
},
"license": "MIT",
"devDependencies": {
"@types/jest": "^23.3.3",
"comment-json": "^1.1.3",
"firebase": "^5.5.3",
"git-cz": "^1.7.1",
"husky": "^1.1.1",
"jest": "^23.6.0",
"mobx": "^5.5.0",
"parcel-bundler": "^1.10.1",
"prettier": "^1.14.3",
"rollup": "^0.66.4",
"rollup-plugin-typescript2": "^0.17.1",
"semantic-release": "^15.9.17",
"ts-jest": "^23.10.3",
"typescript": "^3.1.1"
},
"jest": {
"preset": "ts-jest",
"collectCoverageFrom": [
"src/*.{ts,tsx}"
],
"testPathIgnorePatterns": [
"/node_modules/",
"/build/",
"/dist/"
]
},
"files": [
"dist/"
],
"description": "Tame your firebase database with MobX",
"dependencies": {
"get-firebase-ref": "^1.1.1",
"initialize-firebase-app": "^1.0.0"
}
}
31 changes: 31 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import typescript from "rollup-plugin-typescript2";

export default {
input: "src/index.ts",
output: [
{
file: "dist/index.cjs.js",
format: "cjs"
},
{
file: "dist/index.umd.js",
format: "umd",
name: "TEMP_UMD_MODULE_NAME_CHANGE_ME"
},
{
file: "dist/index.esm.js",
format: "esm"
}
],
plugins: [
typescript({
typescript: require("typescript"),
abortOnError: false,
tsconfigOverride: {
compilerOptions: {
module: "ES2015"
}
}
})
]
};
57 changes: 57 additions & 0 deletions src/__tests__/to-array.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as firebase from "firebase";
import { toJS } from "mobx";
import getMobxFire from "../";
import { config } from "../test-config";

describe("toArray", () => {
const testPath = `mobx-fire/tests/${Date.now()}/`;
const ARRAY_LENGTH = 2;
const listAsObject = Array.from({ length: ARRAY_LENGTH }, (v, i) => {
return {
data: i
};
}).reduce((acc, cur, i) => {
return {
...acc,
[`id_${i}`]: cur
};
}, {}) as any;
const TIMEOUT = 20000;

let { toArray, getFirebaseRef, destroy } = getMobxFire({
firebase,
config: config.client
});
afterAll(async () => {
const ref = getFirebaseRef({ path: `${testPath}` });
await ref.set(null);
destroy();
});
test("exists", () => {
expect(toArray).toBeTruthy();
});
test("works", async () => {
const ref = getFirebaseRef({ path: testPath });
const { value: array, unsub } = toArray(ref);
await ref.set(listAsObject);
expect(toJS(array)).toMatchInlineSnapshot(`
Array [
Object {
"key": "id_0",
"value": Object {
"data": 0,
},
},
Object {
"key": "id_1",
"value": Object {
"data": 1,
},
},
]
`);
await ref.set(null);
expect(toJS(array)).toMatchInlineSnapshot(`Array []`);
unsub();
});
});
97 changes: 97 additions & 0 deletions src/__tests__/to-box.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as firebase from "firebase";
import { config } from "../test-config";
import getMobxFire from "../";
import { waitUntilBox } from "../test-utils";
import { observable } from "mobx";

describe("toBox", () => {
const testPath = `mobx-fire/tests/${Date.now()}/`;
const firstValue = {
val_1: {
i_am_an: "object"
}
};
const firstKey = `1`;
const TIMEOUT = 20000;
const { toBox, getFirebaseRef } = getMobxFire({
firebase,
config: config.client
});
// initializeFirebaseApp({ firebase, ...config.client });
afterAll(async () => {
const ref = getFirebaseRef({ path: `${testPath}` });
await ref.set(null);
});
test("exists", () => {
expect(toBox).toBeTruthy();
});
test("returns the right data", () => {
let ref = getFirebaseRef({ path: `${testPath}/${firstKey}` });
const returnVal = toBox(ref);
expect(returnVal.value.get).toBeTruthy();
expect(returnVal.value.set).toBeTruthy();
expect(returnVal.unsub).toBeInstanceOf(Function);
expect(returnVal.update).toBeInstanceOf(Function);
expect(returnVal.set).toBeInstanceOf(Function);
});

test(
"works with object",
async () => {
let ref = getFirebaseRef({ path: `${testPath}/${firstKey}` });
const { value: box, update } = toBox(ref);
await Promise.all([
update(firstValue),
waitUntilBox(box, ({ oldValue, newValue }) => {
return oldValue !== newValue;
})
]);
expect(box.get()).toEqual(firstValue);
},
TIMEOUT
);
test(
"works with string",
async () => {
const STRING_VALUE = "string";
let ref = getFirebaseRef({ path: `${testPath}/str` });
const { value: box, set } = toBox(ref);
if (box.get() !== null) {
expect(box.get()).toEqual(STRING_VALUE);
}
await Promise.all([
set(STRING_VALUE),
waitUntilBox(box, ({ oldValue, newValue }) => {
// throw newValue;
return oldValue !== newValue;
})
]);
expect(box.get()).toEqual(STRING_VALUE);
},
TIMEOUT
);
test(
"works with custom map",
async () => {
let ref = getFirebaseRef({ path: `${testPath}/asd` });
const { value: box, update } = toBox<{ a: any }>(ref, {
map: value => {
if (!value) return value;
return value.a.toUpperCase();
}
});
if (box.get() !== null) {
expect(box.get()).toEqual({ a: "firstValue" });
return;
}
await Promise.all([
update({ a: "firstValue" }),
waitUntilBox(box, ({ oldValue, newValue }) => {
return oldValue !== newValue;
})
]);
expect(box.get()).toEqual("FIRSTVALUE");
},
TIMEOUT
);
});

0 comments on commit f63aba9

Please sign in to comment.