Skip to content

Commit 8b03d78

Browse files
committed
Updated documentation.
Corrected Timestamp lookup. Fixes jloosli#33
1 parent 5e2fd9d commit 8b03d78

13 files changed

+139
-40
lines changed

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ Exports a json file with the following format:
3737
"docA": {
3838
"name": "Big Co",
3939
"employee_count": 2012,
40+
"created": {
41+
"__datatype__": "timestamp",
42+
"value": {
43+
"_seconds": 12343456,
44+
"_nanoseconds": 7890
45+
}
46+
},
47+
"location": {
48+
"__datatype__": "geopoint",
49+
"value": {
50+
"_latitude": -123.456789,
51+
"_longitude": 34.5678
52+
}
53+
},
54+
"AdministratorRef": {
55+
"__datatype__": "documentReference",
56+
"value": "path/to/the/document"
57+
},
4058
"__collections__": {
4159
"employees": ...,
4260
"products": ...
@@ -60,6 +78,23 @@ Imports need to be from a file with the same structure (e.g. from an exported fi
6078
__Be careful!__ This can easily overwrite or mess up your data if you import
6179
to the wrong location.
6280

81+
#### Special Datatypes
82+
83+
Three types of data are serialized in the export:
84+
85+
* Timestamps
86+
* Geopoints
87+
* DocumentReferences
88+
89+
They each are serialized in the following format:
90+
91+
```json
92+
{
93+
"__datatype__": "timestamp|geopoint|documentReference",
94+
"value": "The serialized value"
95+
}
96+
```
97+
6398
## Installation
6499
Install using [__npm__](https://www.npmjs.com/).
65100

src/interfaces/ICollection.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {IDocument} from "./IDocument";
2+
3+
export interface ICollection {
4+
[id: string]: IDocument;
5+
}

src/interfaces/IDocument.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {ICollection} from "./ICollection";
2+
3+
export interface IDocument {
4+
__collections__: {
5+
[id: string]: ICollection;
6+
};
7+
8+
[id: string]: any;
9+
}

src/interfaces/IDocumentReference.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import {ISpecialType} from "./ISpecialType";
2+
3+
export interface IDocumentReference extends ISpecialType {
4+
__datatype__: 'documentReference';
5+
value: string
6+
}

src/lib/interfaces.ts src/interfaces/IFirebaseCredentials.ts

-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
export interface IDocument {
2-
__collections__: {
3-
[id: string]: ICollection;
4-
};
5-
6-
[id: string]: any;
7-
}
8-
9-
export interface ICollection {
10-
[id: string]: IDocument;
11-
}
12-
131
export interface IFirebaseCredentials {
142
type: string;
153
project_id: string;

src/interfaces/IGeopoint.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {ISpecialType} from "./ISpecialType";
2+
3+
export interface IGeopoint extends ISpecialType {
4+
__datatype__: 'geopoint';
5+
value: {
6+
_latitude: number;
7+
_longitude: number;
8+
}
9+
}

src/interfaces/ISpecialType.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface ISpecialType {
2+
__datatype__: string;
3+
value: any;
4+
}

src/interfaces/ITimestamp.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import {ISpecialType} from "./ISpecialType";
2+
3+
export interface ITimestamp extends ISpecialType {
4+
__datatype__: 'timestamp';
5+
value: {
6+
_seconds: number;
7+
_nanoseconds: number;
8+
}
9+
}

src/lib/firestore-helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as admin from 'firebase-admin';
22
import loadJsonFile from "load-json-file";
3-
import {IFirebaseCredentials} from "./interfaces";
3+
import {IFirebaseCredentials} from "../interfaces/IFirebaseCredentials";
44

55

66
const getCredentialsFromFile = (credentialsFilename: string): Promise<IFirebaseCredentials> => {

src/lib/helpers.ts

+50-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import * as admin from "firebase-admin";
2+
import {ITimestamp} from "../interfaces/ITimestamp";
3+
import {IGeopoint} from "../interfaces/IGeopoint";
4+
import {IDocumentReference} from "../interfaces/IDocumentReference";
25
import DocumentReference = admin.firestore.DocumentReference;
36
import GeoPoint = admin.firestore.GeoPoint;
47
import Firestore = FirebaseFirestore.Firestore;
@@ -16,51 +19,75 @@ const array_chunks = (array: Array<any>, chunk_size: number): Array<Array<any>>
1619
const serializeSpecialTypes = (data: any) => {
1720
const cleaned: any = {};
1821
Object.keys(data).map(key => {
19-
let value = data[key];
20-
if (value instanceof admin.firestore.Timestamp) {
21-
value = {__datatype__: 'timestamp', value: {seconds: value.seconds, nanoseconds: value.nanoseconds}};
22-
} else if (value instanceof GeoPoint) {
23-
value = {__datatype__: 'geopoint', value: value};
24-
} else if (value instanceof DocumentReference) {
25-
value = {__datatype__: 'documentReference', value: value.path};
26-
} else if (value === Object(value)) {
27-
let isArray = Array.isArray(value);
28-
value = serializeSpecialTypes(value);
22+
let rawValue = data[key];
23+
if (rawValue instanceof admin.firestore.Timestamp) {
24+
rawValue = {
25+
__datatype__: 'timestamp',
26+
value: {
27+
_seconds: rawValue.seconds,
28+
_nanoseconds: rawValue.nanoseconds
29+
}
30+
} as ITimestamp;
31+
} else if (rawValue instanceof GeoPoint) {
32+
rawValue = {
33+
__datatype__: 'geopoint',
34+
value: {
35+
_latitude: rawValue.latitude,
36+
_longitude: rawValue.longitude
37+
}
38+
} as IGeopoint;
39+
} else if (rawValue instanceof DocumentReference) {
40+
rawValue = {
41+
__datatype__: 'documentReference',
42+
value: rawValue.path
43+
} as IDocumentReference;
44+
} else if (rawValue === Object(rawValue)) {
45+
let isArray = Array.isArray(rawValue);
46+
rawValue = serializeSpecialTypes(rawValue);
2947
if (isArray) {
30-
value = Object.keys(value).map(key => value[key]);
48+
rawValue = Object.keys(rawValue).map(key => rawValue[key]);
3149
}
3250
}
33-
cleaned[key] = value;
51+
cleaned[key] = rawValue;
3452
});
3553
return cleaned;
3654
};
3755

3856
const unserializeSpecialTypes = (data: any, fs: Firestore) => {
3957
const cleaned: any = {};
4058
Object.keys(data).map(key => {
41-
let value = data[key];
42-
if (value instanceof Object) {
43-
if ('__datatype__' in value && 'value' in value) {
44-
switch (value.__datatype__) {
59+
let rawValue: any = data[key];
60+
let cleanedValue: any;
61+
if (rawValue instanceof Object) {
62+
if ('__datatype__' in rawValue && 'value' in rawValue) {
63+
switch (rawValue.__datatype__) {
4564
case 'timestamp':
46-
value = new admin.firestore.Timestamp(value.value._seconds, value.value._nanoseconds);
65+
rawValue = rawValue as ITimestamp;
66+
if (rawValue.value instanceof String) {
67+
const millis = Date.parse(rawValue.value);
68+
cleanedValue = new admin.firestore.Timestamp(millis / 1000, 0);
69+
} else {
70+
cleanedValue = new admin.firestore.Timestamp(rawValue.value._seconds, rawValue.value._nanoseconds);
71+
}
4772
break;
4873
case 'geopoint':
49-
value = new admin.firestore.GeoPoint(value.value._latitude, value.value._longitude);
74+
rawValue = rawValue as IGeopoint;
75+
cleanedValue = new admin.firestore.GeoPoint(rawValue.value._latitude, rawValue.value._longitude);
5076
break;
5177
case 'documentReference':
52-
value = fs.doc(value.value);
78+
rawValue = rawValue as IDocumentReference;
79+
rawValue = fs.doc(rawValue.value);
5380
break;
5481
}
5582
} else {
56-
let isArray = Array.isArray(value);
57-
value = unserializeSpecialTypes(value, fs);
83+
let isArray = Array.isArray(rawValue);
84+
cleanedValue = unserializeSpecialTypes(rawValue, fs);
5885
if (isArray) {
59-
value = Object.keys(value).map(key => value[key])
86+
cleanedValue = Object.keys(rawValue).map(key => rawValue[key])
6087
}
6188
}
6289
}
63-
cleaned[key] = value;
90+
cleaned[key] = cleanedValue;
6491
});
6592
return cleaned;
6693
};

src/lib/import.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as admin from "firebase-admin";
22
import {isLikeDocument, isRootOfDatabase} from "./firestore-helpers";
3-
import {ICollection} from "./interfaces";
43
import {array_chunks, unserializeSpecialTypes} from "./helpers";
4+
import {ICollection} from "../interfaces/ICollection";
55

66
const importData = (data: any,
77
startingRef: admin.firestore.Firestore |

tests/helpers.spec.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const serialized = {
2727
"__datatype__": "timestamp",
2828
"value": {
2929
"_seconds": 1541579025,
30-
"nanoseconds": 0
30+
"_nanoseconds": 0
3131
}
3232
}
3333
},
@@ -37,15 +37,15 @@ const serialized = {
3737
"__datatype__": "timestamp",
3838
"value": {
3939
"_seconds": 1541579025,
40-
"nanoseconds": 0
40+
"_nanoseconds": 0
4141
}
4242
}
4343
},
4444
"timestamp": {
4545
"__datatype__": "timestamp",
4646
"value": {
4747
"_seconds": 1541579025,
48-
"nanoseconds": 0
48+
"_nanoseconds": 0
4949
}
5050
},
5151
"geopoint": {
@@ -84,7 +84,13 @@ describe('Helpers', () => {
8484
it('should take special types and serialize them as expected.', () => {
8585
const results = serializeSpecialTypes(special);
8686
expect(results.timestamp).to.include.all.keys('__datatype__', 'value');
87+
expect(results.geopoint).to.include.all.keys('__datatype__', 'value');
8788
});
89+
90+
it('should handle timestamp', () => {
91+
const results = serializeSpecialTypes(special);
92+
expect(results.timestamp.value).to.include.all.keys('_seconds', '_nanoseconds');
93+
})
8894
});
8995

9096
// describe('unserializeSpecialTypes', () => {

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"sourceMap": false,
77
"strict": true,
88
"declaration": true,
9+
"noImplicitAny": true,
910
"outDir": "./dist/",
1011
"rootDirs": [
1112
"./src/bin",

0 commit comments

Comments
 (0)