├── .eslintrc.js
├── .gitignore
├── CHANGELOGS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── ISSUE_TEMPLATE.md
├── LICENSE
├── PULL_REQUEST_TEMPLATE.md
├── README.md
├── firestore.gradle
├── jest.config.js
├── package-lock.json
├── package.json
├── plugin.xml
├── src
├── android
│ ├── test
│ │ └── uk
│ │ │ └── co
│ │ │ └── reallysmall
│ │ │ └── cordova
│ │ │ └── plugin
│ │ │ └── firestore
│ │ │ └── LimitQueryHandlerTest.java
│ └── uk
│ │ └── co
│ │ └── reallysmall
│ │ └── cordova
│ │ └── plugin
│ │ └── firestore
│ │ ├── ActionHandler.java
│ │ ├── BatchCommitHandler.java
│ │ ├── BatchDocDeleteHandler.java
│ │ ├── BatchDocSetHandler.java
│ │ ├── BatchDocUpdateHandler.java
│ │ ├── BatchHandler.java
│ │ ├── CollectionAddHandler.java
│ │ ├── CollectionGetHandler.java
│ │ ├── CollectionOnSnapshotHandler.java
│ │ ├── CollectionUnsubscribeHandler.java
│ │ ├── DocDeleteHandler.java
│ │ ├── DocGetHandler.java
│ │ ├── DocOnSnapshotHandler.java
│ │ ├── DocSetHandler.java
│ │ ├── DocSetOptions.java
│ │ ├── DocUnsubscribeHandler.java
│ │ ├── DocUpdateHandler.java
│ │ ├── EndAtQueryHandler.java
│ │ ├── EndBeforeQueryHandler.java
│ │ ├── FieldPathHelper.java
│ │ ├── FieldValueHelper.java
│ │ ├── FirestoreLog.java
│ │ ├── FirestorePlugin.java
│ │ ├── InitialiseHandler.java
│ │ ├── JSONDateWrapper.java
│ │ ├── JSONGeopointWrapper.java
│ │ ├── JSONHelper.java
│ │ ├── JSONReferenceWrapper.java
│ │ ├── JSONTimestampWrapper.java
│ │ ├── LimitQueryHandler.java
│ │ ├── OrderByQueryHandler.java
│ │ ├── PluginResultHelper.java
│ │ ├── QueryHandler.java
│ │ ├── QueryHelper.java
│ │ ├── RunTransactionHandler.java
│ │ ├── StartAfterQueryHandler.java
│ │ ├── StartAtQueryHandler.java
│ │ ├── TransactionDetails.java
│ │ ├── TransactionDocDeleteHandler.java
│ │ ├── TransactionDocGetHandler.java
│ │ ├── TransactionDocSetHandler.java
│ │ ├── TransactionDocUpdateHandler.java
│ │ ├── TransactionOperationType.java
│ │ ├── TransactionQueue.java
│ │ ├── TransactionResolveHandler.java
│ │ └── WhereQueryHandler.java
└── ios
│ ├── FirestorePlugin.h
│ ├── FirestorePlugin.m
│ ├── FirestorePluginJSONHelper.h
│ ├── FirestorePluginJSONHelper.m
│ ├── FirestorePluginResultHelper.h
│ ├── FirestorePluginResultHelper.m
│ ├── FirestoreTransaction.h
│ └── FirestoreTransaction.m
├── types
└── index.d.ts
└── www
├── __mocks__
└── cordova
│ ├── exec.js
│ └── utils.js
├── android_ios
├── batch.js
├── collection_reference.js
├── collection_reference.test.js
├── document_reference.js
├── document_reference.test.js
├── document_snapshot.js
├── firestore.js
├── firestore_timestamp.js
├── geo_point.js
├── path.js
├── path.test.js
├── query.js
├── query_document_snapshot.js
├── query_snapshot.js
├── query_snapshot.test.js
├── transaction.js
├── utilities.js
└── utils.js
└── browser
└── firestore.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true,
6 | "node": true,
7 | "jest": true
8 | },
9 | "extends": "eslint:recommended",
10 | "globals": {
11 | "Atomics": "readonly",
12 | "SharedArrayBuffer": "readonly"
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 2018
16 | },
17 | "rules": {
18 | "accessor-pairs": "error",
19 | "array-bracket-newline": "off",
20 | "array-bracket-spacing": [
21 | "error",
22 | "never"
23 | ],
24 | "array-callback-return": "error",
25 | "array-element-newline": "off",
26 | "arrow-body-style": "error",
27 | "arrow-parens": "error",
28 | "arrow-spacing": [
29 | "error",
30 | {
31 | "after": true,
32 | "before": true
33 | }
34 | ],
35 | "block-scoped-var": "off",
36 | "block-spacing": "error",
37 | "brace-style": [
38 | "error",
39 | "1tbs"
40 | ],
41 | "callback-return": "off",
42 | "camelcase": "error",
43 | "capitalized-comments": [
44 | "error",
45 | "never"
46 | ],
47 | "class-methods-use-this": "error",
48 | "comma-dangle": "error",
49 | "comma-spacing": [
50 | "error",
51 | {
52 | "after": true,
53 | "before": false
54 | }
55 | ],
56 | "comma-style": [
57 | "error",
58 | "last"
59 | ],
60 | "complexity": "error",
61 | "computed-property-spacing": [
62 | "error",
63 | "never"
64 | ],
65 | "consistent-return": "error",
66 | "consistent-this": "error",
67 | "curly": "error",
68 | "default-case": "error",
69 | "dot-location": "error",
70 | "dot-notation": [
71 | "error",
72 | {
73 | "allowKeywords": true
74 | }
75 | ],
76 | "eol-last": "error",
77 | "eqeqeq": "error",
78 | "func-call-spacing": "error",
79 | "func-name-matching": "error",
80 | "func-names": "off",
81 | "func-style": "off",
82 | "function-paren-newline": "error",
83 | "generator-star-spacing": "error",
84 | "global-require": "error",
85 | "guard-for-in": "error",
86 | "handle-callback-err": "off",
87 | "id-blacklist": "error",
88 | "id-length": "off",
89 | "id-match": "error",
90 | "implicit-arrow-linebreak": "error",
91 | "indent": "off",
92 | "indent-legacy": "off",
93 | "init-declarations": "off",
94 | "jsx-quotes": "error",
95 | "key-spacing": "error",
96 | "keyword-spacing": [
97 | "error",
98 | {
99 | "after": true,
100 | "before": true
101 | }
102 | ],
103 | "line-comment-position": "off",
104 | "linebreak-style": [
105 | "error",
106 | "unix"
107 | ],
108 | "lines-around-comment": "error",
109 | "lines-around-directive": "error",
110 | "lines-between-class-members": "error",
111 | "max-classes-per-file": "error",
112 | "max-depth": "error",
113 | "max-len": "off",
114 | "max-lines": "error",
115 | "max-lines-per-function": "error",
116 | "max-nested-callbacks": "error",
117 | "max-params": "error",
118 | "max-statements": "off",
119 | "max-statements-per-line": "error",
120 | "multiline-comment-style": "error",
121 | "new-cap": "error",
122 | "new-parens": "error",
123 | "newline-after-var": "off",
124 | "newline-before-return": "off",
125 | "newline-per-chained-call": "off",
126 | "no-alert": "error",
127 | "no-array-constructor": "error",
128 | "no-async-promise-executor": "error",
129 | "no-await-in-loop": "error",
130 | "no-bitwise": "error",
131 | "no-buffer-constructor": "error",
132 | "no-caller": "error",
133 | "no-catch-shadow": "error",
134 | "no-confusing-arrow": "error",
135 | "no-continue": "error",
136 | "no-div-regex": "error",
137 | "no-duplicate-imports": "error",
138 | "no-else-return": "off",
139 | "no-empty-function": "off",
140 | "no-eq-null": "error",
141 | "no-eval": "error",
142 | "no-extend-native": "off",
143 | "no-extra-bind": "error",
144 | "no-extra-label": "error",
145 | "no-extra-parens": "off",
146 | "no-floating-decimal": "error",
147 | "no-implicit-globals": "error",
148 | "no-implied-eval": "error",
149 | "no-inline-comments": "off",
150 | "no-inner-declarations": [
151 | "error",
152 | "functions"
153 | ],
154 | "no-invalid-this": "error",
155 | "no-iterator": "error",
156 | "no-label-var": "error",
157 | "no-labels": "error",
158 | "no-lone-blocks": "error",
159 | "no-lonely-if": "error",
160 | "no-loop-func": "error",
161 | "no-magic-numbers": "off",
162 | "no-misleading-character-class": "error",
163 | "no-mixed-operators": "error",
164 | "no-mixed-requires": "error",
165 | "no-multi-assign": "error",
166 | "no-multi-spaces": "error",
167 | "no-multi-str": "error",
168 | "no-multiple-empty-lines": "error",
169 | "no-native-reassign": "error",
170 | "no-negated-condition": "error",
171 | "no-negated-in-lhs": "error",
172 | "no-nested-ternary": "error",
173 | "no-new": "error",
174 | "no-new-func": "error",
175 | "no-new-object": "error",
176 | "no-new-require": "error",
177 | "no-new-wrappers": "error",
178 | "no-octal-escape": "error",
179 | "no-param-reassign": "off",
180 | "no-path-concat": "error",
181 | "no-plusplus": [
182 | "error",
183 | {
184 | "allowForLoopAfterthoughts": true
185 | }
186 | ],
187 | "no-process-env": "error",
188 | "no-process-exit": "error",
189 | "no-proto": "error",
190 | "no-prototype-builtins": "error",
191 | "no-restricted-globals": "error",
192 | "no-restricted-imports": "error",
193 | "no-restricted-modules": "error",
194 | "no-restricted-properties": "error",
195 | "no-restricted-syntax": "error",
196 | "no-return-assign": "error",
197 | "no-return-await": "error",
198 | "no-script-url": "error",
199 | "no-self-compare": "error",
200 | "no-sequences": "error",
201 | "no-shadow": "off",
202 | "no-shadow-restricted-names": "error",
203 | "no-spaced-func": "error",
204 | "no-sync": "error",
205 | "no-tabs": "error",
206 | "no-template-curly-in-string": "error",
207 | "no-ternary": "off",
208 | "no-throw-literal": "off",
209 | "no-trailing-spaces": "off",
210 | "no-undef-init": "error",
211 | "no-undefined": "off",
212 | "no-underscore-dangle": "off",
213 | "no-unmodified-loop-condition": "error",
214 | "no-unneeded-ternary": "error",
215 | "no-unused-expressions": "error",
216 | "no-use-before-define": "error",
217 | "no-useless-call": "error",
218 | "no-useless-catch": "error",
219 | "no-useless-computed-key": "error",
220 | "no-useless-concat": "error",
221 | "no-useless-constructor": "error",
222 | "no-useless-rename": "error",
223 | "no-useless-return": "off",
224 | "no-var": "off",
225 | "no-void": "error",
226 | "no-warning-comments": "error",
227 | "no-whitespace-before-property": "error",
228 | "no-with": "error",
229 | "nonblock-statement-body-position": "error",
230 | "object-curly-newline": "error",
231 | "object-curly-spacing": "error",
232 | "object-property-newline": "error",
233 | "object-shorthand": "off",
234 | "one-var": "off",
235 | "one-var-declaration-per-line": "error",
236 | "operator-assignment": "error",
237 | "operator-linebreak": "error",
238 | "padded-blocks": "off",
239 | "padding-line-between-statements": "error",
240 | "prefer-arrow-callback": "off",
241 | "prefer-const": "error",
242 | "prefer-destructuring": "off",
243 | "prefer-named-capture-group": "error",
244 | "prefer-numeric-literals": "error",
245 | "prefer-object-spread": "error",
246 | "prefer-promise-reject-errors": "off",
247 | "prefer-reflect": "off",
248 | "prefer-rest-params": "error",
249 | "prefer-spread": "error",
250 | "prefer-template": "off",
251 | "quote-props": "off",
252 | "quotes": "off",
253 | "radix": [
254 | "error",
255 | "as-needed"
256 | ],
257 | "require-atomic-updates": "error",
258 | "require-await": "error",
259 | "require-jsdoc": "off",
260 | "require-unicode-regexp": "error",
261 | "rest-spread-spacing": "error",
262 | "semi": "error",
263 | "semi-spacing": [
264 | "error",
265 | {
266 | "after": true,
267 | "before": false
268 | }
269 | ],
270 | "semi-style": [
271 | "error",
272 | "last"
273 | ],
274 | "sort-imports": "error",
275 | "sort-keys": "off",
276 | "sort-vars": "error",
277 | "space-before-blocks": "error",
278 | "space-before-function-paren": "off",
279 | "space-in-parens": [
280 | "error",
281 | "never"
282 | ],
283 | "space-infix-ops": "error",
284 | "space-unary-ops": "error",
285 | "spaced-comment": [
286 | "error",
287 | "always"
288 | ],
289 | "strict": [
290 | "error",
291 | "never"
292 | ],
293 | "switch-colon-spacing": "error",
294 | "symbol-description": "error",
295 | "template-curly-spacing": "error",
296 | "template-tag-spacing": "error",
297 | "unicode-bom": [
298 | "error",
299 | "never"
300 | ],
301 | "valid-jsdoc": "error",
302 | "vars-on-top": "off",
303 | "wrap-iife": "error",
304 | "wrap-regex": "error",
305 | "yield-star-spacing": "error",
306 | "yoda": [
307 | "error",
308 | "never"
309 | ]
310 | }
311 | };
312 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .git.safe
2 | /node_modules
3 | .DS_Store
4 | coverage/
5 | .vscode
6 |
--------------------------------------------------------------------------------
/CHANGELOGS.md:
--------------------------------------------------------------------------------
1 | # History
2 | ## 4.1.1
3 | - Fixed Javascript onSnapshot error handling
4 |
5 | ## 4.1.0
6 | - Ensure Android errors are mapped to Javascript
7 | - Ensure iOS errors are mapped to Javascript
8 | - Fix problem with JS snapshot error handlers not being called
9 |
10 | ## 4.0.0
11 | - Amended typescript support
12 | - Updated android firebase dependencies
13 | - Migrate to androidx
14 | - Added in, array-contains and array-contains-any to where method
15 | - Updated Javascript version from 5.5.0 to 7.6.0
16 | - Implemented FieldValue.increment(n), FieldValue.arrayRemove(elements), FieldValue.arrayUnion(elements)
17 | - BREAKING CHANGE: DocumentReference.onSnapshot mimics native Javascritp SDK more accurately
18 | - Add Firestore.totalReads() in Android and iOS to help debug usage
19 |
20 | ## 3.1.0
21 | - Added type definitions
22 |
23 | ## 3.0.0
24 | ### User-facing improvements
25 | - Breaking change: QuerySnapshot.docs() now wraps documents in QueryDocumentSnapshots like firebase-js-sdk
26 | - Revert minimum cordova-ios to 4.5.0 and cordova-android to 7.0.0
27 | - Firestore.doc() improvement, can now call .parent correctly on first-level documents
28 | - CollectionReference.doc() supports id generation when no argument is passed
29 | - Native logs improvement
30 |
31 | ### Technical improvements
32 | - Rename javascript imports to be consistent with file names, this allows running
33 | the JavaScript when running tests
34 | - Add tests for the new features and refactoring
35 |
36 | ## 2.0.0
37 | - Updated README
38 | - Updated Android dependencies
39 | - Added full nested collection implementation - may break backwards compatibility
40 | - Added doc() support to Firestore class
41 | - Add parent to DocumentReference and CollectionReference
42 | - Fix podspec for cordova-ios 5
43 |
44 | ## 1.3.2
45 | - Refactor README
46 | - Reset some significant breaking changes
47 | - Make sure jshint task passes
48 | - Implement Geopoint and Timestamp
49 |
50 | ## 1.3.1
51 | - Organize file structure.
52 | - Normalize initialize options.
53 |
54 | ## 1.3.0
55 | - Merge multi-project config changes
56 | - Merge sub document changes
57 | - Update Web SDK reference to 5.2.0
58 | - Introduce QueryDataSnapshot
59 | - Implement timestampsInSnapshots option in configuration
60 |
61 | ## 1.2.0
62 | - Update Android dependency versions
63 | - Update iOS dependency versions
64 | - Update plugin dependencies
65 | - WARNING: The Android update may require you to update com.google.gms:google-services to 4.0.0, com.android.tools.build:gradle to 3.1.2 and gradle to 4.4.4 (look in platforms/android/cordova/lib/builders/GradleBuilder.js)
66 |
67 | ## 1.1.0
68 | - Add support for FieldValue
69 | - Add experimental support for Transactions. _Please note this is **experimental**!_
70 | - Add startswith polyfill
71 |
72 | ## 1.0.10
73 | - Correct log level when creating results
74 |
75 | ## 1.0.9
76 | - Updated Dependencies
77 | - Remove incorrect Java 7 dependency
78 |
79 | ## 1.0.8
80 | - Ensure dates work for queries and nested data
81 | - Implement delete()
82 | - Update README
83 |
84 | ## 1.0.7
85 | - Remove dependency on cordova-plugin-firebase-hooks
86 |
87 | ## 1.0.6
88 | - Correct README History
89 | - Make browser firebase dependency loading smarter
90 |
91 | ## 1.0.5
92 | ## 1.0.4
93 | ## 1.0.3
94 | - Address plugin dependency issues
95 |
96 | ## 1.0.2
97 | - Updated version
98 | - Added firebase hooks dependency
99 | - Corrected iOS source/header-file config
100 |
101 | ## 1.0.0
102 | Initial release
103 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at richard.windley@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 | We want to make contributing to this project as easy and transparent as possible, whether it's:
3 |
4 | - Reporting a bug
5 | - Discussing the current state of the code
6 | - Submitting a fix
7 | - Proposing new features
8 |
9 | ## We Develop with Github
10 | We use github to host code, to track issues and feature requests, as well as accept pull requests.
11 |
12 | ## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
13 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests:
14 |
15 | 1. Fork the repo and create your branch from `master`.
16 | 2. If you've added code that should be tested, add tests.
17 | 3. If you've changed APIs, update the documentation.
18 | 4. Ensure the test suite passes.
19 | 5. Make sure your code lints.
20 | 6. Issue that pull request!
21 |
22 | ## Any contributions you make will be under the licence specified in the LICENSE file
23 | In short, when you submit code changes, your submissions are understood to be under the same License that covers the project. Feel free to contact the maintainers if that's a concern.
24 |
25 | ## Report bugs using Github's [issues](https://github.com/ReallySmallSoftware/cordova-plugin-firestore/issues)
26 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/ReallySmallSoftware/cordova-plugin-firestore/issues/new); it's that easy!
27 |
28 | ## Write bug reports with detail, background, and sample code where appropriate
29 | Make sure at the very least you provide the information in the [issue template](https://github.com/ReallySmallSoftware/cordova-plugin-firestore/blob/master/ISSUE_TEMPLATE.md)
30 |
31 | ## Use a Consistent Coding Style
32 | The coding style should match what is present in the code currently. Do not reformat entire files.
33 |
34 | ## References
35 | This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/a9316a723f9e918afde44dea68b5f9f39b7d9b00/CONTRIBUTING.md)
36 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | // Project configuration.
4 | grunt.initConfig({
5 | jshint: {
6 | options: {
7 | curly: true,
8 | eqeqeq: true,
9 | immed: true,
10 | latedef: true,
11 | newcap: true,
12 | noarg: true,
13 | sub: true,
14 | undef: true,
15 | boss: true,
16 | eqnull: true,
17 | node: true,
18 | es5: true,
19 | globals: {
20 | jasmine: false,
21 | describe: false,
22 | beforeEach: false,
23 | afterEach: false,
24 | expect: false,
25 | it: false,
26 | spyOn: false,
27 | $: false,
28 | cordova: false,
29 | launchnavigator: false,
30 | window: false,
31 | document: false,
32 | ons: false,
33 | navigator: false,
34 | google: false,
35 | FCMPlugin: false,
36 | device: false,
37 | plugins: false,
38 | addFixture: false,
39 | truncateSql: false
40 | }
41 | },
42 | all: ['Gruntfile.js', 'www/**/*.js']
43 | }
44 | });
45 |
46 | grunt.loadNpmTasks('grunt-contrib-jshint');
47 | };
48 |
--------------------------------------------------------------------------------
/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Expected Behavior
2 |
3 |
4 | ## Actual Behavior
5 |
6 |
7 | ## Steps to Reproduce the Problem
8 |
9 |
10 | ## Specifications
11 |
12 | - Plugin version:
13 | - Framework:
14 | - Framework version:
15 | - Operating system:
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2017 Richard Windley
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Fixes #
2 |
3 | ## Proposed Changes
4 |
5 | -
6 | -
7 | -
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cordova Firestore Plugin
2 | A Google Firebase Firestore plugin to enable realtime synchronisation between app and cloud and automatically handle limited connectivity.
3 |
4 | # What is Firestore?
5 | From the Google documentation (https://firebase.google.com/docs/firestore/):
6 |
7 | > Cloud Firestore is a flexible, scalable database for mobile, web, and server development from Firebase and Google Cloud Platform. Like Firebase Realtime Database, it keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity. Cloud Firestore also offers seamless integration with other Firebase and Google Cloud Platform products, including Cloud Functions
8 |
9 | # Supported platforms
10 | This plugin supports the following platforms:
11 |
12 | - Android
13 | - iOS
14 | - Browser
15 |
16 | # Installation
17 | ## Install the plugin
18 |
19 | ```bash
20 | cordova plugin add cordova-plugin-firestore --save
21 | ```
22 |
23 | or
24 |
25 | ```bash
26 | phonegap plugin add cordova-plugin-firestore
27 | ```
28 |
29 | ### Optional installation variables for Android
30 |
31 | #### ANDROID_FIREBASE_CORE_VERSION
32 | Version of `com.google.firebase:firebase-core`. This defaults to `16.0.8`.
33 |
34 | #### ANDROID_FIREBASE_FIRESTORE_VERSION
35 | Version of `com.google.firebase:firebase-firestore`. This defaults to `18.2.0`.
36 |
37 | You can find the latest versions of these [here](https://firebase.google.com/docs/android/setup#available_libraries).
38 |
39 | ## Android specific installation
40 | Download `google-services.json` to `(your_project_dir)/google-services.json`
41 |
42 | Hint: [Get a config file for your Android App](https://support.google.com/firebase/answer/7015592#android)
43 |
44 | You must ensure that `google-services.json` is put in the correct location. This can be achieved using the following in your `config.xml`:
45 |
46 | ```xml
47 |
48 |
49 |
50 | ```
51 |
52 | ### Dependencies
53 | #### cordova-support-google-services
54 |
55 | In order to ensure Firebase initialises correctly on Android this plugin can be used. This is not automatically added as a dependency to allow for the configuration it performs to be done manually if desired.
56 |
57 | ## iOS specific installation
58 |
59 | Download `GoogleService-Info.plist` to `(your_project_dir)/GoogleService-Info.plist`
60 |
61 | Hint: [Get a config file for your iOS App](https://support.google.com/firebase/answer/7015592#ios)
62 |
63 | You must ensure that `GoogleService-Info.plist` is put in the correct location. This can be done as follows:
64 |
65 | ```xml
66 |
67 |
68 |
69 | ```
70 |
71 | #### Keychain Sharing Capability
72 | If using multiple Firebase plugins it may be necessary to enable this.
73 |
74 | ## Firestore configuration
75 | It is good practice to make sure your Firestore database is only accessible for authorised users, at least for write operations. It is recommended you take time to understand [Firestore rules](https://firebase.google.com/docs/firestore/security/get-started).
76 |
77 | An example that only allows access for authorised users is shown below:
78 |
79 | ```
80 | service cloud.firestore {
81 | match /databases/{database}/documents {
82 | match /{document=**} {
83 | allow read, write : if request.auth != null;
84 | }
85 | }
86 | }
87 | ```
88 |
89 | Authenticating users is beyond the scope of this plugin, but the `cordova-plugin-firebaseui-auth` is one such plugin you can use to achieve this which will work alongside this plugin.
90 |
91 | ## Dependencies
92 | ### Promises
93 | This plugin uses Promises. If you want to use this with Android 4.4 then you will need to include a `Promise` polyfill.
94 |
95 |
96 | # Example
97 | A simple example is shown below which sets up the necessary options, initialises the database and then adds a document to a `users` collection:
98 |
99 | ```js
100 | var options = {
101 | "datePrefix": '__DATE:',
102 | "fieldValueDelete": "__DELETE",
103 | "fieldValueServerTimestamp" : "__SERVERTIMESTAMP",
104 | "persist": true,
105 | "config" : {}
106 | };
107 |
108 | if (cordova.platformId === "browser") {
109 |
110 | options.config = {
111 | apiKey: "(your api key)",
112 | authDomain: "localhost",
113 | projectId: "(your project id)"
114 | };
115 | }
116 |
117 | Firestore.initialise(options).then(function(db) {
118 | // Add a second document with a generated ID.
119 | db.get().collection("users").add({
120 | first: "Alan",
121 | middle: "Mathison",
122 | last: "Turing",
123 | born: 1912
124 | })
125 | .then(function(docRef) {
126 | console.log("Document written with ID: ", docRef.id);
127 | })
128 | .catch(function(error) {
129 | console.error("Error adding document: ", error);
130 | });
131 | });
132 | ```
133 |
134 | ## options.config
135 | In the above example this is being used for the browser version, but it can also be used for Android and iOS to specify different databases than the default in the `google-services.json` and `GoogleService-Info.plist` files.
136 |
137 | # What is supported?
138 |
139 | ## Firestore
140 | - collection(collectionPath)
141 | - runTransaction(updateFunction)
142 | - doc(id)
143 |
144 | ## DocumentSnapshot and QueryDataSnapshot
145 | - data()
146 | - get(fieldPath)
147 | - exists
148 | - id
149 | - ref
150 |
151 | ## QuerySnapshot
152 | - docs
153 | - empty
154 | - size
155 |
156 | ## DocumentReference
157 | - collection(collectionPath)
158 | - delete()
159 | - get()
160 | - onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError)
161 | - set(data, options)
162 | - update(data)
163 | - id
164 | - parent
165 |
166 | ## Query
167 | - endAt(snapshotOrVarArgs)
168 | - endBefore(snapshotOrVarArgs)
169 | - limit(limit)
170 | - orderBy(field, direction)
171 | - get()
172 | - onSnapshot(optionsOrObserverOrOnNext, observerOrOnNextOrOnError, onError)
173 | - startAfter(snapshotOrVarArgs)
174 | - startAt(snapshotOrVarArgs)
175 | - where(fieldPath, opStr, passedValue)
176 |
177 | ## CollectionReference (inherits from Query)
178 | - add(data)
179 | - id
180 | - doc(id)
181 | - parent
182 |
183 | ## Transaction
184 | - get()
185 | - delete()
186 | - set()
187 | - update()
188 |
189 | ## FieldValue
190 | - FieldValue.delete()
191 | - FieldValue.serverTimestamp()
192 |
193 | ## GeoPoint
194 | - Firestore.GeoPoint(latitude, longitude)
195 |
196 | ## Timestamp
197 | - Firestore.Timestamp(seconds, nanoseconds)
198 |
199 | ## Dates
200 | Because data is transferred to the client as JSON there is extra logic in place to handle the conversion of dates for some operations.
201 |
202 | When initialising the plugin you can set up a prefix that is applied to a string value which is used to identify it as a date. The default prefix is `__DATE:`
203 |
204 | Therefore, when a date field is retrieved from the database by the native code it will be transferred in the JSON looking similar to the following:
205 |
206 | ```json
207 | {
208 | "dateField" : "__DATE:123456789"
209 | }
210 | ```
211 |
212 | The number is seconds since epoch.
213 |
214 | The client will receive the field as a Javascript Date.
215 |
216 | This conversion also happens when specifying a field in a where condition.
217 |
218 | ### timestampsInSnapshots
219 | By default this option is set to `false` to mirror the current default. [This explains the setting](https://firebase.google.com/docs/reference/js/firebase.firestore.Settings).
220 |
221 | Not setting this to `true` will result in the following message when running in the browser:
222 |
223 | ```
224 | The behavior for Date objects stored in Firestore is going to change
225 | AND YOUR APP MAY BREAK.
226 | To hide this warning and ensure your app does not break, you need to add the
227 | following code to your app before calling any other Cloud Firestore methods:
228 |
229 | const firestore = firebase.firestore();
230 | const settings = {/* your settings... */ timestampsInSnapshots: true};
231 | firestore.settings(settings);
232 |
233 | With this change, timestamps stored in Cloud Firestore will be read back as
234 | Firebase Timestamp objects instead of as system Date objects. So you will also
235 | need to update code expecting a Date to instead expect a Timestamp. For example:
236 |
237 | // Old:
238 | const date = snapshot.get('created_at');
239 | // New:
240 | const timestamp = snapshot.get('created_at');
241 | const date = timestamp.toDate();
242 |
243 | Please audit all existing usages of Date when you enable the new behavior. In a
244 | future release, the behavior will change to the new behavior, so if you do not
245 | follow these steps, YOUR APP MAY BREAK.
246 | ```
247 |
248 | ## FieldValue constants
249 | Similar to the situation with dates, there are special values used for `FieldValue` values:
250 |
251 | - FieldValue.delete() equates to `__DELETE`
252 | - FieldValue.serverTimestamp() equates to `__SERVERTIMESTAMP`
253 |
254 | These values can be changed when initialisation is performed.
255 |
256 | ## Nested collections
257 | Nested collections are supported. This can take either form as follows:
258 |
259 | ```
260 | db.get().collection("mycollection").doc("mydoc").collection("mysubcollection");
261 | db.get().collection("mycollection/mydoc/mysubcollection");
262 | ```
263 |
264 | Note that the second form is slightly more efficient as it results in less objects being instantiated.
265 |
266 | ## Typescript
267 | Support is now included for typescript. Use the following to reference the typescript definitions:
268 |
269 | ```
270 | ///
271 | e
272 | private static crashlytics: FirebaseCrashlytics = FirebaseCrashlyticsPlugin.initialise();
273 | crashlytics.logException("my message");
274 | ```
275 |
276 | You may also need to add an external to webpack.config.ls:
277 |
278 | ```
279 | externals: {
280 | 'cordova-plugin-firebase-crashlytics': "cordova-plugin-firebase-crashlytics"
281 | '/exec':"cordova/exec"
282 | },
283 | ```
284 |
285 | ## Learnings and notes
286 | I have learnt a number of things whilst implementing this:
287 | - The documentation states that the database cannot be initialised in a seperate thread when using persistence. In my experience this should say it cannot be *used* in multiple threads.
288 | - When used on Android ensure that at least `com.google.gms:google-services:3.1.1` is used in build dependencies. Earlier versions did not work for me.
289 | - Yes, I did spell initialise() with an 's' - Original plugin developer @ReallySmallSoftware is from the UK
290 |
--------------------------------------------------------------------------------
/firestore.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | maven { url 'https://maven.google.com' }
4 | mavenLocal()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.3.2'
9 | classpath 'com.google.gms:google-services:4.2.0'
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | // For a detailed explanation regarding each configuration property, visit:
2 | // https://jestjs.io/docs/en/configuration.html
3 |
4 | module.exports = {
5 | // All imported modules in your tests should be mocked automatically
6 | // automock: false,
7 |
8 | // Stop running tests after `n` failures
9 | // bail: 0,
10 |
11 | // Respect "browser" field in package.json when resolving modules
12 | // browser: false,
13 |
14 | // The directory where Jest should store its cached dependency information
15 | // cacheDirectory: "/tmp/jest_rs",
16 |
17 | // Automatically clear mock calls and instances between every test
18 | clearMocks: true,
19 |
20 | // Indicates whether the coverage information should be collected while executing the test
21 | // collectCoverage: false,
22 |
23 | // An array of glob patterns indicating a set of files for which coverage information should be collected
24 | // collectCoverageFrom: null,
25 |
26 | // The directory where Jest should output its coverage files
27 | coverageDirectory: "coverage",
28 |
29 | // An array of regexp pattern strings used to skip coverage collection
30 | // coveragePathIgnorePatterns: [
31 | // "/node_modules/"
32 | // ],
33 |
34 | // A list of reporter names that Jest uses when writing coverage reports
35 | // coverageReporters: [
36 | // "json",
37 | // "text",
38 | // "lcov",
39 | // "clover"
40 | // ],
41 |
42 | // An object that configures minimum threshold enforcement for coverage results
43 | // coverageThreshold: null,
44 |
45 | // A path to a custom dependency extractor
46 | // dependencyExtractor: null,
47 |
48 | // Make calling deprecated APIs throw helpful error messages
49 | // errorOnDeprecated: false,
50 |
51 | // Force coverage collection from ignored files using an array of glob patterns
52 | // forceCoverageMatch: [],
53 |
54 | // A path to a module which exports an async function that is triggered once before all test suites
55 | // globalSetup: null,
56 |
57 | // A path to a module which exports an async function that is triggered once after all test suites
58 | // globalTeardown: null,
59 |
60 | // A set of global variables that need to be available in all test environments
61 | // globals: {},
62 |
63 | // An array of directory names to be searched recursively up from the requiring module's location
64 | // moduleDirectories: [
65 | // "node_modules"
66 | // ],
67 |
68 | // An array of file extensions your modules use
69 | // moduleFileExtensions: [
70 | // "js",
71 | // "json",
72 | // "jsx",
73 | // "ts",
74 | // "tsx",
75 | // "node"
76 | // ],
77 |
78 | // A map from regular expressions to module names that allow to stub out resources with a single module
79 | // moduleNameMapper: {},
80 |
81 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
82 | // modulePathIgnorePatterns: [],
83 |
84 | // Activates notifications for test results
85 | // notify: false,
86 |
87 | // An enum that specifies notification mode. Requires { notify: true }
88 | // notifyMode: "failure-change",
89 |
90 | // A preset that is used as a base for Jest's configuration
91 | // preset: null,
92 |
93 | // Run tests from one or more projects
94 | // projects: null,
95 |
96 | // Use this configuration option to add custom reporters to Jest
97 | // reporters: undefined,
98 |
99 | // Automatically reset mock state between every test
100 | // resetMocks: false,
101 |
102 | // Reset the module registry before running each individual test
103 | // resetModules: false,
104 |
105 | // A path to a custom resolver
106 | // resolver: null,
107 |
108 | // Automatically restore mock state between every test
109 | // restoreMocks: false,
110 |
111 | // The root directory that Jest should scan for tests and modules within
112 | // rootDir: null,
113 |
114 | // A list of paths to directories that Jest should use to search for files in
115 | // roots: [
116 | // ""
117 | // ],
118 |
119 | // Allows you to use a custom runner instead of Jest's default test runner
120 | // runner: "jest-runner",
121 |
122 | // The paths to modules that run some code to configure or set up the testing environment before each test
123 | // setupFiles: [],
124 |
125 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
126 | // setupFilesAfterEnv: [],
127 |
128 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
129 | // snapshotSerializers: [],
130 |
131 | // The test environment that will be used for testing
132 | testEnvironment: "node",
133 |
134 | // Options that will be passed to the testEnvironment
135 | // testEnvironmentOptions: {},
136 |
137 | // Adds a location field to test results
138 | // testLocationInResults: false,
139 |
140 | // The glob patterns Jest uses to detect test files
141 | // testMatch: [
142 | // "**/__tests__/**/*.[jt]s?(x)",
143 | // "**/?(*.)+(spec|test).[tj]s?(x)"
144 | // ],
145 |
146 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
147 | // testPathIgnorePatterns: [
148 | // "/node_modules/"
149 | // ],
150 |
151 | // The regexp pattern or array of patterns that Jest uses to detect test files
152 | // testRegex: [],
153 |
154 | // This option allows the use of a custom results processor
155 | // testResultsProcessor: null,
156 |
157 | // This option allows use of a custom test runner
158 | // testRunner: "jasmine2",
159 |
160 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
161 | // testURL: "http://localhost",
162 |
163 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
164 | // timers: "real",
165 |
166 | // A map from regular expressions to paths to transformers
167 | // transform: null,
168 |
169 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
170 | // transformIgnorePatterns: [
171 | // "/node_modules/"
172 | // ],
173 |
174 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
175 | // unmockedModulePathPatterns: undefined,
176 |
177 | // Indicates whether each individual test should be reported during the run
178 | // verbose: null,
179 |
180 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
181 | // watchPathIgnorePatterns: [],
182 |
183 | // Whether to use watchman for file crawling
184 | // watchman: true,
185 | };
186 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-firestore",
3 | "version": "4.1.1",
4 | "cordova": {
5 | "id": "cordova-plugin-firestore",
6 | "platforms": [
7 | "android",
8 | "ios",
9 | "browser"
10 | ]
11 | },
12 | "types": "./types/index.d.ts",
13 | "engines": {
14 | "cordovaDependencies": {
15 | "0.0.7": {
16 | "cordova": ">=7.0.0",
17 | "cordova-android": ">=5.0.0",
18 | "cordova-ios": ">=4.0.0"
19 | }
20 | }
21 | },
22 | "description": "A Google Firebase Firestore plugin",
23 | "devDependencies": {
24 | "grunt": "^1.0.4",
25 | "grunt-contrib-jshint": "^1.1.0",
26 | "jest": "^24.7.1"
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "https://github.com/ReallySmallSoftware/cordova-plugin-firestore.git"
31 | },
32 | "author": "Richard Windley (http://www.reallysmall.co.uk)",
33 | "license": "Apache-2.0",
34 | "bugs": {
35 | "url": "https://github.com/ReallySmallSoftware/cordova-plugin-firestore/issues"
36 | },
37 | "homepage": "https://github.com/ReallySmallSoftware/cordova-plugin-firestore",
38 | "keywords": [
39 | "ecosystem:cordova",
40 | "cordova-android",
41 | "cordova-ios",
42 | "cordova-browser"
43 | ],
44 | "scripts": {
45 | "test": "jest"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/android/test/uk/co/reallysmall/cordova/plugin/firestore/LimitQueryHandlerTest.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.Query;
4 |
5 | import org.junit.Test;
6 |
7 | import static org.mockito.Mockito.mock;
8 | import static org.mockito.Mockito.verify;
9 |
10 | public class LimitQueryHandlerTest {
11 | LimitQueryHandler limitQueryHandler = new LimitQueryHandler();
12 |
13 | Query query = mock(Query.class);
14 |
15 |
16 | @Test
17 | public void testShouldHandleStringArgument() {
18 | limitQueryHandler.handle(query, "10");
19 | verify(query).limit(10);
20 | }
21 |
22 | @Test
23 | public void testShouldHandleIntArgument() {
24 | limitQueryHandler.handle(query, 10);
25 | verify(query).limit(10);
26 | }
27 |
28 | @Test(expected = IllegalArgumentException.class)
29 | public void testShouldThrowIllegalArgumentExceptiononArrayArgument() {
30 | limitQueryHandler.handle(query, new String[5]);
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/ActionHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import org.apache.cordova.CallbackContext;
4 | import org.json.JSONArray;
5 | import org.json.JSONException;
6 |
7 | public interface ActionHandler {
8 | boolean handle(JSONArray args, final CallbackContext callbackContext) throws JSONException;
9 | }
10 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/BatchCommitHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | import com.google.android.gms.tasks.OnCompleteListener;
7 | import com.google.android.gms.tasks.Task;
8 | import com.google.firebase.firestore.FirebaseFirestoreException;
9 | import com.google.firebase.firestore.WriteBatch;
10 |
11 | import org.apache.cordova.CallbackContext;
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 |
15 | public class BatchCommitHandler implements ActionHandler {
16 | private final FirestorePlugin firestorePlugin;
17 |
18 | public BatchCommitHandler(FirestorePlugin firestorePlugin) {
19 | this.firestorePlugin = firestorePlugin;
20 | }
21 |
22 | @Override
23 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
24 | try {
25 | final String batchId = args.getString(0);
26 |
27 | FirestoreLog.d(FirestorePlugin.TAG, String.format("Batch commit for %s", batchId));
28 |
29 | WriteBatch batch = this.firestorePlugin.getBatch(batchId);
30 |
31 | batch.commit().addOnCompleteListener(new OnCompleteListener() {
32 | @Override
33 | public void onComplete(@NonNull Task task) {
34 | firestorePlugin.removeBatch((batchId));
35 |
36 | if (task.isSuccessful()) {
37 | callbackContext.success();
38 | } else {
39 | String errorCode = ((FirebaseFirestoreException) task.getException()).getCode().name();
40 | callbackContext.error(PluginResultHelper.createError(errorCode, task.getException().getMessage()));
41 | }
42 | }
43 | });
44 |
45 | } catch (JSONException e) {
46 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing batch commit", e);
47 | callbackContext.error(e.getMessage());
48 | }
49 |
50 | return true;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/BatchDocDeleteHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 |
4 | import com.google.firebase.firestore.DocumentReference;
5 | import com.google.firebase.firestore.WriteBatch;
6 |
7 | import org.apache.cordova.CallbackContext;
8 | import org.json.JSONArray;
9 | import org.json.JSONException;
10 |
11 | public class BatchDocDeleteHandler implements ActionHandler {
12 | private final FirestorePlugin firestorePlugin;
13 |
14 | public BatchDocDeleteHandler(FirestorePlugin firestorePlugin) {
15 | this.firestorePlugin = firestorePlugin;
16 | }
17 |
18 | @Override
19 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
20 | try {
21 | final String batchId = args.getString(0);
22 | final String docId = args.getString(1);
23 | final String collectionPath = args.getString(2);
24 |
25 | FirestoreLog.d(FirestorePlugin.TAG, String.format("Batch document delete for %s", batchId));
26 |
27 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(docId);
28 |
29 | WriteBatch batch = this.firestorePlugin.getBatch(batchId);
30 | batch.delete(documentRef);
31 |
32 | callbackContext.success();
33 |
34 | } catch (JSONException e) {
35 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing batch document delete", e);
36 | callbackContext.error(e.getMessage());
37 | }
38 |
39 | return true;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/BatchDocSetHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.DocumentReference;
4 | import com.google.firebase.firestore.SetOptions;
5 | import com.google.firebase.firestore.WriteBatch;
6 |
7 | import org.apache.cordova.CallbackContext;
8 | import org.json.JSONArray;
9 | import org.json.JSONException;
10 | import org.json.JSONObject;
11 |
12 | public class BatchDocSetHandler implements ActionHandler {
13 | private final FirestorePlugin firestorePlugin;
14 |
15 | public BatchDocSetHandler(FirestorePlugin firestorePlugin) {
16 | this.firestorePlugin = firestorePlugin;
17 | }
18 |
19 | @Override
20 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
21 | try {
22 | final String batchId = args.getString(0);
23 | final String docId = args.getString(1);
24 | final String collectionPath = args.getString(2);
25 | final JSONObject data = args.getJSONObject(3);
26 |
27 | final JSONObject options;
28 |
29 | if (!args.isNull(4)) {
30 | options = args.getJSONObject(4);
31 | } else {
32 | options = null;
33 | }
34 |
35 | FirestoreLog.d(FirestorePlugin.TAG, String.format("Batch document set for %s", batchId));
36 |
37 | SetOptions setOptions = DocSetOptions.getSetOptions(options);
38 |
39 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(docId);
40 |
41 | WriteBatch batch = this.firestorePlugin.getBatch(batchId);
42 |
43 | if (setOptions == null) {
44 | batch.set(documentRef, JSONHelper.fromJSON(data));
45 | } else {
46 | batch.set(documentRef, JSONHelper.fromJSON(data), setOptions);
47 | }
48 |
49 | callbackContext.success();
50 |
51 | } catch (JSONException e) {
52 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing batch document set", e);
53 | callbackContext.error(e.getMessage());
54 | }
55 |
56 | return true;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/BatchDocUpdateHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.DocumentReference;
4 | import com.google.firebase.firestore.SetOptions;
5 | import com.google.firebase.firestore.WriteBatch;
6 |
7 | import org.apache.cordova.CallbackContext;
8 | import org.json.JSONArray;
9 | import org.json.JSONException;
10 | import org.json.JSONObject;
11 |
12 | import java.util.Map;
13 |
14 | public class BatchDocUpdateHandler implements ActionHandler {
15 | private final FirestorePlugin firestorePlugin;
16 |
17 | public BatchDocUpdateHandler(FirestorePlugin firestorePlugin) {
18 | this.firestorePlugin = firestorePlugin;
19 | }
20 |
21 | @Override
22 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
23 | try {
24 | final String batchId = args.getString(0);
25 | final String docId = args.getString(1);
26 | final String collectionPath = args.getString(2);
27 | final JSONObject data = args.getJSONObject(3);
28 |
29 | FirestoreLog.d(FirestorePlugin.TAG, String.format("Batch document set for %s", batchId));
30 |
31 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(docId);
32 |
33 | WriteBatch batch = this.firestorePlugin.getBatch(batchId);
34 |
35 | batch.update(documentRef, (Map)JSONHelper.fromJSON(data));
36 |
37 | callbackContext.success();
38 |
39 | } catch (JSONException e) {
40 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing batch document update", e);
41 | callbackContext.error(e.getMessage());
42 | }
43 |
44 | return true;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/BatchHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 |
4 | import com.google.firebase.firestore.WriteBatch;
5 |
6 | import org.apache.cordova.CallbackContext;
7 | import org.json.JSONArray;
8 | import org.json.JSONException;
9 |
10 | public class BatchHandler implements ActionHandler {
11 | private final FirestorePlugin firestorePlugin;
12 |
13 | public BatchHandler(FirestorePlugin firestorePlugin) {
14 | this.firestorePlugin = firestorePlugin;
15 | }
16 |
17 | @Override
18 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
19 | try {
20 | final String batchId = args.getString(0);
21 |
22 | FirestoreLog.d(FirestorePlugin.TAG, String.format("Batch %s", batchId));
23 |
24 | WriteBatch batch = this.firestorePlugin.getDatabase().batch();
25 | this.firestorePlugin.storeBatch(batchId,batch);
26 |
27 | callbackContext.success();
28 |
29 | } catch (JSONException e) {
30 | FirestoreLog.e(FirestorePlugin.TAG, "Error creating batch", e);
31 | callbackContext.error(e.getMessage());
32 | }
33 |
34 | return true;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/CollectionAddHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | //import android.support.annotation.NonNull;
4 | import androidx.annotation.NonNull;
5 |
6 | import android.util.Log;
7 |
8 | import com.google.android.gms.tasks.OnFailureListener;
9 | import com.google.android.gms.tasks.OnSuccessListener;
10 | import com.google.firebase.firestore.DocumentReference;
11 | import com.google.firebase.firestore.FirebaseFirestoreException;
12 |
13 | import org.apache.cordova.CallbackContext;
14 | import org.json.JSONArray;
15 | import org.json.JSONException;
16 | import org.json.JSONObject;
17 |
18 | public class CollectionAddHandler implements ActionHandler {
19 |
20 | private FirestorePlugin firestorePlugin;
21 |
22 | public CollectionAddHandler(FirestorePlugin firestorePlugin) {
23 | this.firestorePlugin = firestorePlugin;
24 | }
25 |
26 | @Override
27 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
28 | try {
29 | final String collectionPath = args.getString(0);
30 | final JSONObject data = args.getJSONObject(1);
31 |
32 | FirestoreLog.d(FirestorePlugin.TAG, "Writing document to collection");
33 |
34 | try {
35 | firestorePlugin.getDatabase().collection(collectionPath).add(JSONHelper.fromJSON(data)).addOnSuccessListener(new OnSuccessListener() {
36 | @Override
37 | public void onSuccess(DocumentReference documentReference) {
38 | callbackContext.sendPluginResult(PluginResultHelper.createPluginResult(documentReference, false));
39 |
40 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully written document to collection");
41 | }
42 | }).addOnFailureListener(new OnFailureListener() {
43 | @Override
44 | public void onFailure(@NonNull Exception e) {
45 |
46 | FirestoreLog.w(FirestorePlugin.TAG, "Error writing document to collection", e);
47 | String errorCode = ((FirebaseFirestoreException) e).getCode().name();
48 | callbackContext.error(PluginResultHelper.createError(errorCode, e.getMessage()));
49 | }
50 | });
51 | } catch (Exception e) {
52 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection add in thread", e);
53 | callbackContext.error(e.getMessage());
54 | }
55 | } catch (JSONException e) {
56 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection add", e);
57 | callbackContext.error(e.getMessage());
58 | }
59 |
60 | return true;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/CollectionGetHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | //import android.support.annotation.NonNull;
4 | import androidx.annotation.NonNull;
5 |
6 | import android.util.Log;
7 |
8 | import com.google.android.gms.tasks.OnFailureListener;
9 | import com.google.android.gms.tasks.OnSuccessListener;
10 | import com.google.firebase.firestore.CollectionReference;
11 | import com.google.firebase.firestore.FirebaseFirestoreException;
12 | import com.google.firebase.firestore.Query;
13 | import com.google.firebase.firestore.QuerySnapshot;
14 |
15 | import org.apache.cordova.CallbackContext;
16 | import org.json.JSONArray;
17 | import org.json.JSONException;
18 |
19 | import static uk.co.reallysmall.cordova.plugin.firestore.PluginResultHelper.createPluginResult;
20 |
21 | public class CollectionGetHandler implements ActionHandler {
22 |
23 | private FirestorePlugin firestorePlugin;
24 |
25 | public CollectionGetHandler(FirestorePlugin firestorePlugin) {
26 | this.firestorePlugin = firestorePlugin;
27 | }
28 |
29 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
30 | try {
31 | final String collectionPath = args.getString(0);
32 | final JSONArray queries = args.getJSONArray(1);
33 |
34 |
35 | FirestoreLog.d(FirestorePlugin.TAG, "Getting document from collection");
36 |
37 | try {
38 | CollectionReference collectionRef = firestorePlugin.getDatabase().collection(collectionPath);
39 | Query query = QueryHelper.processQueries(queries, collectionRef, this.firestorePlugin);
40 |
41 | query.get().addOnSuccessListener(new OnSuccessListener() {
42 | @Override
43 | public void onSuccess(QuerySnapshot querySnapshot) {
44 | callbackContext.sendPluginResult(createPluginResult(querySnapshot, false));
45 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully got collection");
46 | }
47 | }).addOnFailureListener(new OnFailureListener() {
48 | @Override
49 | public void onFailure(@NonNull Exception e) {
50 | FirestoreLog.w(FirestorePlugin.TAG, "Error getting collection", e);
51 | String errorCode = ((FirebaseFirestoreException) e).getCode().name();
52 | callbackContext.error(PluginResultHelper.createError(errorCode, e.getMessage()));
53 | }
54 | });
55 | } catch (Exception e) {
56 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection get in thread", e);
57 | callbackContext.error(e.getMessage());
58 | }
59 |
60 | } catch (JSONException e) {
61 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection get", e);
62 | callbackContext.error(e.getMessage());
63 | }
64 |
65 | return true;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/CollectionOnSnapshotHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import androidx.annotation.Nullable;
4 | import android.util.Log;
5 |
6 | import com.google.firebase.firestore.CollectionReference;
7 | import com.google.firebase.firestore.EventListener;
8 | import com.google.firebase.firestore.FirebaseFirestoreException;
9 | import com.google.firebase.firestore.Query;
10 | import com.google.firebase.firestore.MetadataChanges;
11 | import com.google.firebase.firestore.QuerySnapshot;
12 |
13 | import org.apache.cordova.CallbackContext;
14 | import org.json.JSONArray;
15 | import org.json.JSONException;
16 | import org.json.JSONObject;
17 |
18 | public class CollectionOnSnapshotHandler implements ActionHandler {
19 | private FirestorePlugin firestorePlugin;
20 |
21 | public CollectionOnSnapshotHandler(FirestorePlugin firestorePlugin) {
22 | this.firestorePlugin = firestorePlugin;
23 | }
24 |
25 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
26 | try {
27 | final String collection = args.getString(0);
28 | final JSONArray queries = args.getJSONArray(1);
29 | final JSONObject options = args.optJSONObject(2);
30 | final String callbackId = args.getString(3);
31 |
32 |
33 | FirestoreLog.d(FirestorePlugin.TAG, "Listening to collection " + collection);
34 |
35 | try {
36 | CollectionReference collectionRef = firestorePlugin.getDatabase().collection(collection);
37 | MetadataChanges metadataChanges = getMetadataChanges(options);
38 |
39 | Query query = QueryHelper.processQueries(queries, collectionRef, this.firestorePlugin);
40 |
41 | EventListener eventListener = new EventListener() {
42 | @Override
43 | public void onEvent(@Nullable QuerySnapshot value,
44 | @Nullable FirebaseFirestoreException e) {
45 |
46 | if (e != null) {
47 | FirestoreLog.w(FirestorePlugin.TAG, "Collection snapshot listener error " + collection, e);
48 | callbackContext.sendPluginResult(PluginResultHelper.createPluginErrorResult(e, true));
49 | return;
50 | }
51 |
52 | FirestoreLog.d(FirestorePlugin.TAG, "Got collection snapshot data " + collection);
53 | callbackContext.sendPluginResult(PluginResultHelper.createPluginResult(value, true));
54 | }
55 | };
56 |
57 | firestorePlugin.addRegistration(callbackId, query.addSnapshotListener(metadataChanges, eventListener));
58 |
59 | } catch (Exception e) {
60 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection snapshot in thread " + collection, e);
61 | callbackContext.error(e.getMessage());
62 | }
63 |
64 | } catch (JSONException e) {
65 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing collection snapshot", e);
66 | callbackContext.error(e.getMessage());
67 | }
68 |
69 | return true;
70 | }
71 |
72 | private MetadataChanges getMetadataChanges(JSONObject options) {
73 | MetadataChanges metadataChanges = MetadataChanges.EXCLUDE;
74 |
75 | if (options != null) {
76 |
77 | try {
78 | if (options.getBoolean("includeDocumentMetadataChanges")) {
79 | metadataChanges = MetadataChanges.INCLUDE;
80 | }
81 | if (options.getBoolean("includeQueryMetadataChanges")) {
82 | metadataChanges = MetadataChanges.INCLUDE;
83 | }
84 | if (options.getBoolean("includeMetadataChanges")) {
85 | metadataChanges = MetadataChanges.INCLUDE;
86 | }
87 | } catch (JSONException e) {
88 | FirestoreLog.e(FirestorePlugin.TAG, "Error getting query options", e);
89 | throw new RuntimeException(e);
90 | }
91 |
92 | FirestoreLog.d(FirestorePlugin.TAG, "Set document options");
93 | }
94 |
95 | return metadataChanges;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/CollectionUnsubscribeHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import android.util.Log;
4 |
5 | import org.apache.cordova.CallbackContext;
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 |
9 | public class CollectionUnsubscribeHandler implements ActionHandler {
10 |
11 | private FirestorePlugin firestorePlugin;
12 |
13 | public CollectionUnsubscribeHandler(FirestorePlugin firestorePlugin) {
14 | this.firestorePlugin = firestorePlugin;
15 | }
16 |
17 | @Override
18 | public boolean handle(JSONArray args, CallbackContext callbackContext) {
19 | try {
20 | String callbackId = args.getString(0);
21 | firestorePlugin.unregister(callbackId);
22 | callbackContext.success();
23 | } catch (JSONException e) {
24 | FirestoreLog.e(FirestorePlugin.TAG, "Error unsubscribing from collection", e);
25 | callbackContext.error(e.getMessage());
26 | }
27 |
28 | return true;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocDeleteHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | //import android.support.annotation.NonNull;
4 | import androidx.annotation.NonNull;
5 | import android.util.Log;
6 |
7 | import com.google.android.gms.tasks.OnFailureListener;
8 | import com.google.android.gms.tasks.OnSuccessListener;
9 | import com.google.firebase.firestore.DocumentReference;
10 |
11 | import org.apache.cordova.CallbackContext;
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 |
15 | public class DocDeleteHandler implements ActionHandler {
16 |
17 | private FirestorePlugin firestorePlugin;
18 |
19 | public DocDeleteHandler(FirestorePlugin firestorePlugin) {
20 | this.firestorePlugin = firestorePlugin;
21 | }
22 |
23 | @Override
24 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
25 | try {
26 | final String collectionPath = args.getString(0);
27 | final String doc = args.getString(1);
28 |
29 | FirestoreLog.d(FirestorePlugin.TAG, "Deleting document");
30 |
31 | try {
32 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(doc);
33 | FirestoreLog.d(FirestorePlugin.TAG, "Get for document " + collectionPath + "/" + doc);
34 |
35 | documentRef.delete().addOnSuccessListener(new OnSuccessListener() {
36 | @Override
37 | public void onSuccess(Void aVoid) {
38 | callbackContext.success(0);
39 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully deleted document");
40 | }
41 | }).addOnFailureListener(new OnFailureListener() {
42 | @Override
43 | public void onFailure(@NonNull Exception e) {
44 | FirestoreLog.w(FirestorePlugin.TAG, "Error deleting document", e);
45 | callbackContext.error(e.getMessage());
46 | }
47 | });
48 | } catch (Exception e) {
49 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document delete in thread", e);
50 | callbackContext.error(e.getMessage());
51 | }
52 |
53 | } catch (JSONException e) {
54 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document delete", e);
55 | callbackContext.error(e.getMessage());
56 | }
57 |
58 | return true;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocGetHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 |
4 | //import android.support.annotation.NonNull;
5 | import androidx.annotation.NonNull;
6 |
7 | import android.util.Log;
8 |
9 | import com.google.android.gms.tasks.OnFailureListener;
10 | import com.google.android.gms.tasks.OnSuccessListener;
11 | import com.google.firebase.firestore.DocumentReference;
12 | import com.google.firebase.firestore.DocumentSnapshot;
13 | import com.google.firebase.firestore.FirebaseFirestoreException;
14 |
15 | import org.apache.cordova.CallbackContext;
16 | import org.json.JSONArray;
17 | import org.json.JSONException;
18 |
19 | import static uk.co.reallysmall.cordova.plugin.firestore.PluginResultHelper.createPluginResult;
20 |
21 | public class DocGetHandler implements ActionHandler {
22 |
23 | private FirestorePlugin firestorePlugin;
24 |
25 | public DocGetHandler(FirestorePlugin firestorePlugin) {
26 | this.firestorePlugin = firestorePlugin;
27 | }
28 |
29 | @Override
30 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
31 | try {
32 | final String collectionPath = args.getString(0);
33 | final String docId = args.getString(1);
34 | final String docPath = collectionPath + "/" + docId;
35 | FirestoreLog.d(FirestorePlugin.TAG, "Getting document : " + docPath);
36 |
37 | try {
38 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(docId);
39 |
40 | documentRef.get().addOnSuccessListener(new OnSuccessListener() {
41 | @Override
42 | public void onSuccess(DocumentSnapshot documentSnapshot) {
43 | callbackContext.sendPluginResult(createPluginResult(documentSnapshot, false));
44 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully got document " + docPath);
45 | }
46 | }).addOnFailureListener(new OnFailureListener() {
47 | @Override
48 | public void onFailure(@NonNull Exception e) {
49 | FirestoreLog.w(FirestorePlugin.TAG, "Error getting document " + docPath, e);
50 | String errorCode = ((FirebaseFirestoreException) e).getCode().name();
51 | callbackContext.error(PluginResultHelper.createError(errorCode, e.getMessage())); }
52 | });
53 | } catch (Exception e) {
54 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document get " + docPath, e);
55 | callbackContext.error(e.getMessage());
56 | }
57 |
58 | } catch (JSONException e) {
59 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document snapshot ", e);
60 | callbackContext.error(e.getMessage());
61 | }
62 |
63 | return true;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocOnSnapshotHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 |
4 | import androidx.annotation.Nullable;
5 | import android.util.Log;
6 |
7 | import com.google.firebase.firestore.DocumentReference;
8 | import com.google.firebase.firestore.DocumentSnapshot;
9 | import com.google.firebase.firestore.EventListener;
10 | import com.google.firebase.firestore.FirebaseFirestoreException;
11 | import com.google.firebase.firestore.MetadataChanges;
12 |
13 | import org.apache.cordova.CallbackContext;
14 | import org.json.JSONArray;
15 | import org.json.JSONException;
16 | import org.json.JSONObject;
17 |
18 | public class DocOnSnapshotHandler implements ActionHandler {
19 | private FirestorePlugin firestorePlugin;
20 |
21 | public DocOnSnapshotHandler(FirestorePlugin firestorePlugin) {
22 | this.firestorePlugin = firestorePlugin;
23 | }
24 |
25 | @Override
26 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
27 | try {
28 | final String collectionPath = args.getString(0);
29 | final String docId = args.getString(1);
30 | final String docPath = collectionPath + "/" + docId;
31 | final String callbackId = args.getString(2);
32 |
33 | final JSONObject options;
34 |
35 | if (args.length() > 3) {
36 | options = args.getJSONObject(3);
37 | } else {
38 | options = null;
39 | }
40 |
41 | DocumentReference documentRef = firestorePlugin.getDatabase().collection(collectionPath).document(docId);
42 | MetadataChanges metadataChanges = getMetadataChanges(options);
43 |
44 | FirestoreLog.d(FirestorePlugin.TAG, "Launching onSnapshot handler for document " + docPath);
45 |
46 | EventListener eventListener = new EventListener() {
47 | @Override
48 | public void onEvent(@Nullable DocumentSnapshot documentSnapshot,
49 | @Nullable FirebaseFirestoreException e) {
50 | if (e != null) {
51 | FirestoreLog.w(FirestorePlugin.TAG, "Document snapshot listener error", e);
52 | callbackContext.sendPluginResult(PluginResultHelper.createPluginErrorResult(e, true));
53 | return;
54 | }
55 |
56 | FirestoreLog.d(FirestorePlugin.TAG, "Got document snapshot data");
57 | callbackContext.sendPluginResult(PluginResultHelper.createPluginResult(documentSnapshot, true));
58 | }
59 | };
60 |
61 | firestorePlugin.addRegistration(callbackId, documentRef.addSnapshotListener(metadataChanges, eventListener));
62 |
63 | } catch (JSONException e) {
64 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document snapshot", e);
65 | callbackContext.error(e.getMessage());
66 | }
67 |
68 | return true;
69 | }
70 |
71 | private MetadataChanges getMetadataChanges(JSONObject options) {
72 | MetadataChanges metadataChanges = MetadataChanges.EXCLUDE;
73 |
74 | if (options != null) {
75 |
76 | try {
77 | if (options.getBoolean("includeMetadataChanges")) {
78 | metadataChanges = MetadataChanges.INCLUDE;
79 | }
80 | } catch (JSONException e) {
81 | FirestoreLog.e(FirestorePlugin.TAG, "Error getting document option includeMetadataChanges", e);
82 | throw new RuntimeException(e);
83 | }
84 |
85 | FirestoreLog.d(FirestorePlugin.TAG, "Set document options");
86 | }
87 |
88 | return metadataChanges;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocSetHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | //import android.support.annotation.NonNull;
4 | import androidx.annotation.NonNull;
5 |
6 | import android.util.Log;
7 |
8 | import com.google.android.gms.tasks.OnFailureListener;
9 | import com.google.android.gms.tasks.OnSuccessListener;
10 | import com.google.firebase.firestore.DocumentReference;
11 | import com.google.firebase.firestore.FirebaseFirestoreException;
12 | import com.google.firebase.firestore.SetOptions;
13 |
14 | import org.apache.cordova.CallbackContext;
15 | import org.json.JSONArray;
16 | import org.json.JSONException;
17 | import org.json.JSONObject;
18 |
19 | public class DocSetHandler implements ActionHandler {
20 |
21 | private FirestorePlugin firestorePlugin;
22 |
23 | public DocSetHandler(FirestorePlugin firestorePlugin) {
24 | this.firestorePlugin = firestorePlugin;
25 | }
26 |
27 | @Override
28 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
29 | try {
30 | final String collection = args.getString(0);
31 | final String docId = args.getString(1);
32 | final String docPath = collection + "/" + docId;
33 | final JSONObject data = args.getJSONObject(2);
34 |
35 | final JSONObject options;
36 |
37 | if (!args.isNull(3)) {
38 | options = args.getJSONObject(3);
39 | } else {
40 | options = null;
41 | }
42 |
43 | try {
44 |
45 | SetOptions setOptions = DocSetOptions.getSetOptions(options);
46 |
47 | FirestoreLog.d(FirestorePlugin.TAG, "Setting document " + docPath);
48 |
49 | DocumentReference documentReference = firestorePlugin.getDatabase().collection(collection).document(docId);
50 |
51 | OnSuccessListener onSuccessListener = new OnSuccessListener() {
52 | @Override
53 | public void onSuccess(Void aVoid) {
54 | callbackContext.success();
55 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully written document " + docPath);
56 | }
57 | };
58 |
59 | OnFailureListener onFailureListener = new OnFailureListener() {
60 | @Override
61 | public void onFailure(@NonNull Exception e) {
62 | FirestoreLog.w(FirestorePlugin.TAG, "Error writing document " + docPath, e);
63 | String errorCode = ((FirebaseFirestoreException) e).getCode().name();
64 | callbackContext.error(PluginResultHelper.createError(errorCode, e.getMessage())); }
65 | };
66 |
67 | if (setOptions == null) {
68 | documentReference.set(JSONHelper.fromJSON(data)).addOnSuccessListener(onSuccessListener).addOnFailureListener(onFailureListener);
69 | } else {
70 | documentReference.set(JSONHelper.fromJSON(data), setOptions).addOnSuccessListener(onSuccessListener).addOnFailureListener(onFailureListener);
71 | }
72 | } catch (Exception e) {
73 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document set " + docPath, e);
74 | callbackContext.error(e.getMessage());
75 | }
76 | } catch (JSONException e) {
77 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document set", e);
78 | callbackContext.error(e.getMessage());
79 | }
80 |
81 | return true;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocSetOptions.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import android.util.Log;
4 |
5 | import com.google.firebase.firestore.SetOptions;
6 |
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | public class DocSetOptions {
11 |
12 | public static SetOptions getSetOptions(JSONObject options) {
13 | SetOptions setOptions = null;
14 |
15 | try {
16 | if (options != null && options.getBoolean("merge")) {
17 | setOptions = SetOptions.merge();
18 | }
19 | } catch (JSONException e) {
20 | FirestoreLog.e(FirestorePlugin.TAG, "Error getting document option", e);
21 | throw new RuntimeException(e);
22 | }
23 |
24 | FirestoreLog.d(FirestorePlugin.TAG, "Set document options");
25 |
26 | return setOptions;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocUnsubscribeHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import android.util.Log;
4 |
5 | import org.apache.cordova.CallbackContext;
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 |
9 | public class DocUnsubscribeHandler implements ActionHandler {
10 |
11 | private FirestorePlugin firestorePlugin;
12 |
13 | public DocUnsubscribeHandler(FirestorePlugin firestorePlugin) {
14 | this.firestorePlugin = firestorePlugin;
15 | }
16 |
17 | @Override
18 | public boolean handle(JSONArray args, CallbackContext callbackContext) {
19 | try {
20 | String callbackId = args.getString(0);
21 | firestorePlugin.unregister(callbackId);
22 | callbackContext.success();
23 | } catch (JSONException e) {
24 | FirestoreLog.e(FirestorePlugin.TAG, "Error unsubscribing from document", e);
25 | callbackContext.error(e.getMessage());
26 | }
27 |
28 | return true;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/DocUpdateHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | //import android.support.annotation.NonNull;
4 | import androidx.annotation.NonNull;
5 | import android.util.Log;
6 |
7 | import com.google.android.gms.tasks.OnFailureListener;
8 | import com.google.android.gms.tasks.OnSuccessListener;
9 | import com.google.firebase.firestore.FirebaseFirestoreException;
10 |
11 | import org.apache.cordova.CallbackContext;
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 | import org.json.JSONObject;
15 |
16 | import java.util.Map;
17 |
18 |
19 | public class DocUpdateHandler implements ActionHandler {
20 | private FirestorePlugin firestorePlugin;
21 |
22 | public DocUpdateHandler(FirestorePlugin firestorePlugin) {
23 | this.firestorePlugin = firestorePlugin;
24 | }
25 |
26 | @Override
27 | public boolean handle(JSONArray args, final CallbackContext callbackContext) {
28 | try {
29 | final String collection = args.getString(0);
30 | final String docId = args.getString(1);
31 | final JSONObject data = args.getJSONObject(2);
32 |
33 |
34 | FirestoreLog.d(FirestorePlugin.TAG, "Updating document");
35 |
36 | try {
37 | firestorePlugin.getDatabase().collection(collection).document(docId).update((Map)JSONHelper.fromJSON(data)).addOnSuccessListener(new OnSuccessListener() {
38 | @Override
39 | public void onSuccess(Void aVoid) {
40 | callbackContext.success();
41 | FirestoreLog.d(FirestorePlugin.TAG, "Successfully updated document");
42 | }
43 | }).addOnFailureListener(new OnFailureListener() {
44 | @Override
45 | public void onFailure(@NonNull Exception e) {
46 | String errorCode = ((FirebaseFirestoreException) e).getCode().name();
47 | callbackContext.error(PluginResultHelper.createError(errorCode, e.getMessage()));
48 | FirestoreLog.w(FirestorePlugin.TAG, "Error updating document", e);
49 | }
50 | });
51 | } catch (Exception e) {
52 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document update in thread", e);
53 | callbackContext.error(e.getMessage());
54 | }
55 | ;
56 | } catch (JSONException e) {
57 | FirestoreLog.e(FirestorePlugin.TAG, "Error processing document update", e);
58 | callbackContext.error(e.getMessage());
59 | }
60 |
61 | return true;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/EndAtQueryHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.Query;
4 |
5 | public class EndAtQueryHandler implements QueryHandler {
6 | @Override
7 | public Query handle(Query query, Object endAt) {
8 | return query.endAt(JSONHelper.fromJSON(endAt));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/EndBeforeQueryHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.Query;
4 |
5 | public class EndBeforeQueryHandler implements QueryHandler {
6 | @Override
7 | public Query handle(Query query, Object endBefore) {
8 | return query.endBefore(JSONHelper.fromJSON(endBefore));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/FieldPathHelper.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.FieldPath;
4 |
5 | public class FieldPathHelper {
6 |
7 | private static String fieldPathDocumentId = "__DOCUMENTID";
8 |
9 | public static void setDocumentIdePrefix(String fieldPathDocumentId) {
10 | FieldPathHelper.fieldPathDocumentId = fieldPathDocumentId;
11 | }
12 |
13 | public static boolean isWrapped(String value) {
14 | if (fieldPathDocumentId.equals(value)) {
15 | return true;
16 | }
17 | return false;
18 | }
19 |
20 | public static FieldPath unwrapFieldPath(Object value) {
21 | return FieldPath.documentId();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/FieldValueHelper.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.FieldValue;
4 |
5 | import org.json.JSONArray;
6 | import org.json.JSONObject;
7 |
8 | public class FieldValueHelper {
9 |
10 | private static String fieldValueDelete = "__DELETE";
11 | private static String fieldValueServerTimestamp = "__SERVERTIMESTAMP";
12 | private static String fieldValueIncrement = "__INCREMENT";
13 | private static String fieldValueArrayRemove = "__ARRAYREMOVE";
14 | private static String fieldValueArrayUnion = "__ARRAYUNION";
15 |
16 | public static void setDeletePrefix(String fieldValueDelete) {
17 | FieldValueHelper.fieldValueDelete = fieldValueDelete;
18 | }
19 |
20 | public static void setServerTimestampPrefix(String fieldValueServerTimestamp) {
21 | FieldValueHelper.fieldValueServerTimestamp = fieldValueServerTimestamp;
22 | }
23 |
24 | public static void setIncrementPrefix(String fieldValueIncrement) {
25 | FieldValueHelper.fieldValueIncrement = fieldValueIncrement;
26 | }
27 |
28 | public static void setArrayRemovePrefix(String fieldValueArrayRemove) {
29 | FieldValueHelper.fieldValueArrayRemove = fieldValueArrayRemove;
30 | }
31 |
32 | public static void setArrayUnionPrefix(String fieldValueArrayUnion) {
33 | FieldValueHelper.fieldValueArrayUnion = fieldValueArrayUnion;
34 | }
35 |
36 | public static Object unwrapFieldValue(Object value) {
37 | String valueString = (String) value;
38 |
39 | if (fieldValueDelete.equals(valueString)) {
40 | return FieldValue.delete();
41 | }
42 |
43 | if (fieldValueServerTimestamp.equals(valueString)) {
44 | return FieldValue.serverTimestamp();
45 | }
46 |
47 | if (valueString.startsWith(fieldValueIncrement)) {
48 | return FieldValue.increment(Long.parseLong(FieldValueHelper.unwrap(valueString, fieldValueIncrement)));
49 | }
50 |
51 | if (valueString.startsWith(fieldValueArrayRemove)) {
52 | String unwrapped = FieldValueHelper.unwrap(valueString, fieldValueArrayRemove);
53 |
54 | return FieldValue.arrayRemove(JSONArrayToArray(unwrapped));
55 | }
56 |
57 | if (valueString.startsWith(fieldValueArrayUnion)) {
58 | String unwrapped = FieldValueHelper.unwrap(valueString, fieldValueArrayUnion);
59 |
60 | return FieldValue.arrayUnion(JSONArrayToArray(unwrapped));
61 | }
62 |
63 | return value;
64 | }
65 |
66 | private static Object[] JSONArrayToArray(String unwrapped) {
67 |
68 | try {
69 | JSONArray jsonArray = new JSONArray(unwrapped);
70 |
71 | if (jsonArray == null)
72 | return null;
73 |
74 | Object[] array = new Object[jsonArray.length()];
75 | for (int i = 0; i < array.length; i++) {
76 | array[i] = jsonArray.opt(i);
77 | }
78 | return array;
79 | } catch (Exception ex) {
80 | return null;
81 | }
82 | }
83 |
84 | public static boolean isWrappedFieldValue(Object value) {
85 | if (value instanceof String) {
86 | String valueString = (String) value;
87 |
88 | if (fieldValueDelete.equals(valueString)) {
89 | return true;
90 | }
91 |
92 | if (fieldValueServerTimestamp.equals(valueString)) {
93 | return true;
94 | }
95 |
96 | if (valueString.startsWith(fieldValueIncrement)) {
97 | return true;
98 | }
99 |
100 | if (valueString.startsWith(fieldValueArrayRemove)) {
101 | return true;
102 | }
103 |
104 | if (valueString.startsWith(fieldValueArrayUnion)) {
105 | return true;
106 | }
107 | }
108 |
109 | return false;
110 | }
111 |
112 | public static String unwrap(String valueString, String prefix) {
113 | int prefixLength = prefix.length();
114 | String ret = valueString.substring(prefixLength + 1);
115 | return ret;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/FirestoreLog.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import android.util.Log;
4 |
5 | public class FirestoreLog {
6 |
7 | private static int _logLevel = Log.DEBUG;
8 |
9 | public static void setLogLevel(String logLevel) {
10 | FirestoreLog.w(FirestorePlugin.TAG, "Setting log level to : " + logLevel);
11 | switch(logLevel.toLowerCase()) {
12 | case "debug":
13 | _logLevel = Log.DEBUG;
14 | break;
15 | case "error":
16 | _logLevel = Log.ERROR;
17 | break;
18 | case "warn":
19 | case "warning":
20 | _logLevel = Log.WARN;
21 | break;
22 | default:
23 | FirestoreLog.e(FirestorePlugin.TAG, "New logLevel unknown, leaving previous setting : " + _logLevel);
24 | break;
25 | }
26 |
27 | FirestoreLog.d(FirestorePlugin.TAG, "New logLevel : " + _logLevel);
28 | }
29 |
30 | public static void d(String TAG, String message) {
31 | if (_logLevel <= Log.DEBUG) {
32 | Log.d(TAG, message);
33 | }
34 | }
35 | public static void w(String TAG, String message) {
36 | if (_logLevel <= Log.WARN) {
37 | Log.w(TAG, message);
38 | }
39 | }
40 | public static void w(String TAG, String message, Exception e) {
41 | if (_logLevel <= Log.WARN) {
42 | Log.w(TAG, message, e);
43 | }
44 | }
45 | public static void e(String TAG, String message) {
46 | if (_logLevel <= Log.ERROR) {
47 | Log.e(TAG, message);
48 | }
49 | }
50 | public static void e(String TAG, String message, Exception e) {
51 | if (_logLevel <= Log.ERROR) {
52 | Log.e(TAG, message, e);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/FirestorePlugin.java:
--------------------------------------------------------------------------------
1 | /**
2 | */
3 | package uk.co.reallysmall.cordova.plugin.firestore;
4 |
5 | import android.util.Log;
6 |
7 | import com.google.firebase.firestore.FirebaseFirestore;
8 | import com.google.firebase.firestore.ListenerRegistration;
9 | import com.google.firebase.firestore.Transaction;
10 | import com.google.firebase.firestore.WriteBatch;
11 |
12 | import org.apache.cordova.CallbackContext;
13 | import org.apache.cordova.CordovaInterface;
14 | import org.apache.cordova.CordovaPlugin;
15 | import org.apache.cordova.CordovaWebView;
16 | import org.json.JSONArray;
17 | import org.json.JSONException;
18 |
19 | import java.util.Hashtable;
20 | import java.util.Map;
21 |
22 | public class FirestorePlugin extends CordovaPlugin {
23 | static final String TAG = "FirestorePlugin";
24 | private static FirebaseFirestore database;
25 | private Map handlers = new Hashtable();
26 | private Map registrations = new Hashtable();
27 | private Map transactions = new Hashtable();
28 | private Map batches = new Hashtable();
29 |
30 | public void initialize(CordovaInterface cordova, CordovaWebView webView) {
31 | FirestoreLog.d(TAG, "Initializing FirestorePlugin");
32 | super.initialize(cordova, webView);
33 |
34 | handlers.put("collectionOnSnapshot", new CollectionOnSnapshotHandler(FirestorePlugin.this));
35 | handlers.put("collectionUnsubscribe", new CollectionUnsubscribeHandler(FirestorePlugin.this));
36 | handlers.put("collectionAdd", new CollectionAddHandler(FirestorePlugin.this));
37 | handlers.put("collectionGet", new CollectionGetHandler(FirestorePlugin.this));
38 | handlers.put("initialise", new InitialiseHandler(webView.getContext().getApplicationContext(), FirestorePlugin.this));
39 | handlers.put("docSet", new DocSetHandler(FirestorePlugin.this));
40 | handlers.put("docUpdate", new DocUpdateHandler(FirestorePlugin.this));
41 | handlers.put("docOnSnapshot", new DocOnSnapshotHandler(FirestorePlugin.this));
42 | handlers.put("docUnsubscribe", new DocUnsubscribeHandler(FirestorePlugin.this));
43 | handlers.put("docGet", new DocGetHandler(FirestorePlugin.this));
44 | handlers.put("docDelete", new DocDeleteHandler(FirestorePlugin.this));
45 | handlers.put("runTransaction", new RunTransactionHandler(FirestorePlugin.this));
46 | handlers.put("transactionDocGet", new TransactionDocGetHandler(FirestorePlugin.this));
47 | handlers.put("transactionDocUpdate", new TransactionDocUpdateHandler(FirestorePlugin.this));
48 | handlers.put("transactionDocSet", new TransactionDocSetHandler(FirestorePlugin.this));
49 | handlers.put("transactionDocDelete", new TransactionDocDeleteHandler(FirestorePlugin.this));
50 | handlers.put("transactionResolve", new TransactionResolveHandler(FirestorePlugin.this));
51 | handlers.put("setLogLevel", new setLogLevel());
52 | handlers.put("batch", new BatchHandler(FirestorePlugin.this));
53 | handlers.put("batchUpdate", new BatchDocUpdateHandler(FirestorePlugin.this));
54 | handlers.put("batchSet", new BatchDocSetHandler(FirestorePlugin.this));
55 | handlers.put("batchDelete", new BatchDocDeleteHandler(FirestorePlugin.this));
56 | handlers.put("batchCommit", new BatchCommitHandler(FirestorePlugin.this));
57 |
58 | FirestoreLog.d(TAG, "Done Initializing FirestorePlugin");
59 | }
60 |
61 | public FirebaseFirestore getDatabase() {
62 | return database;
63 | }
64 |
65 | public void setDatabase(FirebaseFirestore database) {
66 | this.database = database;
67 | }
68 |
69 | public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
70 | FirestoreLog.d(TAG, action);
71 |
72 | if (handlers.containsKey(action)) {
73 | return handlers.get(action).handle(args, callbackContext);
74 | }
75 |
76 | return false;
77 | }
78 |
79 | public void addRegistration(String callbackId, ListenerRegistration listenerRegistration) {
80 | registrations.put(callbackId, listenerRegistration);
81 | FirestoreLog.d(TAG, "Registered subscriber " + callbackId);
82 |
83 | }
84 |
85 | public void unregister(String callbackId) {
86 | if (registrations.containsKey(callbackId)) {
87 | registrations.get(callbackId).remove();
88 | registrations.remove(callbackId);
89 | FirestoreLog.d(TAG, "Unregistered subscriber " + callbackId);
90 | }
91 | }
92 |
93 | public void storeTransaction(String transactionId, Transaction transaction) {
94 | TransactionQueue transactionQueue = new TransactionQueue();
95 | transactionQueue.transaction = transaction;
96 | transactions.put(transactionId, transactionQueue);
97 | }
98 |
99 | public TransactionQueue getTransaction(String transactionId) {
100 | return transactions.get(transactionId);
101 | }
102 |
103 | public void removeTransaction(String transactionId) {
104 | transactions.remove(transactionId);
105 | }
106 |
107 | public void storeBatch(String batchId, WriteBatch batch) {
108 | batches.put(batchId, batch);
109 | }
110 |
111 | public WriteBatch getBatch(String batchId) {
112 | return batches.get(batchId);
113 | }
114 |
115 | public void removeBatch(String batchId) {
116 | batches.remove(batchId);
117 | }
118 |
119 | private class setLogLevel implements ActionHandler {
120 |
121 | @Override
122 | public boolean handle(JSONArray args, CallbackContext callbackContext) throws JSONException {
123 | FirestoreLog.setLogLevel(args.getString(0));
124 | callbackContext.success();
125 | return true;
126 | }
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/InitialiseHandler.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import com.google.firebase.FirebaseApp;
7 | import com.google.firebase.FirebaseOptions;
8 | import com.google.firebase.firestore.FirebaseFirestore;
9 | import com.google.firebase.firestore.FirebaseFirestoreSettings;
10 |
11 | import org.apache.cordova.CallbackContext;
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 | import org.json.JSONObject;
15 |
16 |
17 | public class InitialiseHandler implements ActionHandler {
18 |
19 | public static final String PERSIST = "persist";
20 | public static final String DATE_PREFIX = "datePrefix";
21 | public static final String TIMESTAMP_PREFIX = "timestampPrefix";
22 | public static final String GEOPOINT_PREFIX = "geopointPrefix";
23 | public static final String REFERENCE_PREFIX = "referencePrefix";
24 | public static final String FIELDVALUE_DELETE = "fieldValueDelete";
25 | public static final String FIELDVALUE_SERVERTIMESTAMP = "fieldValueServerTimestamp";
26 | public static final String FIELDVALUE_ARRAYREMOVE = "fieldValueArrayRemove";
27 | public static final String FIELDVALUE_ARRAYUNION = "fieldValueArrayUnion";
28 | public static final String FIELDVALUE_INCREMENT = "fieldValueIncrement";
29 | public static final String TIMESTAMPSINSNAPSHOTS = "timestampsInSnapshots";
30 | public static final String CONFIG = "config";
31 | private FirestorePlugin firestorePlugin;
32 | private Context context;
33 |
34 | public InitialiseHandler(Context context, FirestorePlugin firestorePlugin) {
35 | this.context = context;
36 | this.firestorePlugin = firestorePlugin;
37 | }
38 |
39 | @Override
40 | public boolean handle(final JSONArray args, CallbackContext callbackContext) {
41 |
42 | try {
43 |
44 | FirestoreLog.d(FirestorePlugin.TAG, "Initialising Firestore...");
45 |
46 | final JSONObject options = args.getJSONObject(0);
47 | FirestoreLog.d(FirestorePlugin.TAG, "Options : " + options.toString());
48 |
49 | FirebaseFirestore.setLoggingEnabled(true);
50 | if (options.has(CONFIG)) {
51 | JSONObject config = options.getJSONObject(CONFIG);
52 | FirebaseOptions.Builder configBuilder = new FirebaseOptions.Builder();
53 | if (options.has("applicationId")) {
54 | configBuilder.setApplicationId(options.getString("applicationId"));
55 | }
56 | if (options.has("gcmSenderId")) {
57 | configBuilder.setGcmSenderId(options.getString("gcmSenderId"));
58 | }
59 | if (options.has("apiKey")) {
60 | configBuilder.setApiKey(options.getString("apiKey"));
61 | }
62 | if (options.has("projectId")) {
63 | configBuilder.setProjectId(options.getString("projectId"));
64 | }
65 | if (options.has("databaseUrl")) {
66 | configBuilder.setDatabaseUrl(options.getString("databaseUrl"));
67 | }
68 | if (options.has("storageBucket")) {
69 | configBuilder.setStorageBucket(options.getString("storageBucket"));
70 | }
71 |
72 | FirebaseOptions customOptions = configBuilder.build();
73 |
74 | FirebaseApp customApp;
75 | try {
76 | customApp = FirebaseApp.getInstance(config.getString("apiKey"));
77 | } catch (Exception err) {
78 | FirebaseApp.initializeApp(this.context, customOptions, config.getString("apiKey"));
79 | customApp = FirebaseApp.getInstance(config.getString("apiKey"));
80 | err.printStackTrace();
81 | }
82 |
83 | firestorePlugin.setDatabase(FirebaseFirestore.getInstance(customApp));
84 | } else{
85 | firestorePlugin.setDatabase(FirebaseFirestore.getInstance());
86 | }
87 |
88 | boolean persist = false;
89 |
90 | if (options.has(PERSIST) && options.getBoolean(PERSIST)) {
91 | persist = true;
92 | }
93 |
94 | if (options.has(DATE_PREFIX)) {
95 | JSONDateWrapper.setDatePrefix(options.getString(DATE_PREFIX));
96 | }
97 |
98 | if (options.has(TIMESTAMP_PREFIX)) {
99 | JSONTimestampWrapper.setTimestampPrefix(options.getString(TIMESTAMP_PREFIX));
100 | }
101 |
102 | if (options.has(GEOPOINT_PREFIX)) {
103 | JSONGeopointWrapper.setGeopointPrefix(options.getString(GEOPOINT_PREFIX));
104 | }
105 |
106 | if (options.has(REFERENCE_PREFIX)) {
107 | JSONReferenceWrapper.setReferencePrefix(options.getString(REFERENCE_PREFIX));
108 | }
109 |
110 | if (options.has(FIELDVALUE_DELETE)) {
111 | FieldValueHelper.setDeletePrefix(options.getString(FIELDVALUE_DELETE));
112 | }
113 |
114 | if (options.has(FIELDVALUE_SERVERTIMESTAMP)) {
115 | FieldValueHelper.setServerTimestampPrefix(options.getString(FIELDVALUE_SERVERTIMESTAMP));
116 | }
117 |
118 | if (options.has(FIELDVALUE_ARRAYREMOVE)) {
119 | FieldValueHelper.setArrayRemovePrefix(options.getString(FIELDVALUE_ARRAYREMOVE));
120 | }
121 |
122 | if (options.has(FIELDVALUE_ARRAYUNION)) {
123 | FieldValueHelper.setArrayUnionPrefix(options.getString(FIELDVALUE_ARRAYUNION));
124 | }
125 |
126 | if (options.has(FIELDVALUE_INCREMENT)) {
127 | FieldValueHelper.setIncrementPrefix(options.getString(FIELDVALUE_INCREMENT));
128 | }
129 |
130 | JSONHelper.setPlugin(this.firestorePlugin);
131 |
132 | FirestoreLog.d(FirestorePlugin.TAG, "Setting Firestore persistance to " + persist);
133 |
134 | boolean timestampsInSnapshots = false;
135 |
136 | if (options.has(TIMESTAMPSINSNAPSHOTS)) {
137 | timestampsInSnapshots = options.getBoolean(TIMESTAMPSINSNAPSHOTS);
138 | }
139 |
140 | FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
141 | .setPersistenceEnabled(persist)
142 | .setTimestampsInSnapshotsEnabled(timestampsInSnapshots)
143 | .build();
144 | firestorePlugin.getDatabase().setFirestoreSettings(settings);
145 |
146 | callbackContext.success();
147 | } catch (JSONException e) {
148 | FirestoreLog.e(FirestorePlugin.TAG, "Error initialising Firestore", e);
149 | callbackContext.error(e.getMessage());
150 | }
151 |
152 | return true;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONDateWrapper.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.GeoPoint;
4 |
5 | import java.util.Date;
6 |
7 | public class JSONDateWrapper extends Date {
8 |
9 | private static String datePrefix = "__DATE:";
10 |
11 | public JSONDateWrapper(Date date) {
12 | super(date.getTime());
13 | }
14 |
15 | public static void setDatePrefix(String datePrefix) {
16 | JSONDateWrapper.datePrefix = datePrefix;
17 | }
18 |
19 | public static boolean isWrappedDate(Object value) {
20 |
21 |
22 | if (value instanceof String && ((String) value).startsWith(datePrefix)) {
23 | return true;
24 | }
25 |
26 | return false;
27 | }
28 |
29 | public static Date unwrapDate(Object value) {
30 | String stringValue = (String) value;
31 | int prefixLength = datePrefix.length();
32 | String timestamp = stringValue.substring(prefixLength + 1);
33 |
34 | return new Date(Long.parseLong(timestamp));
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | return this.datePrefix + this.getTime();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONGeopointWrapper.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.GeoPoint;
4 |
5 | import java.util.Date;
6 |
7 | public class JSONGeopointWrapper extends GeoPoint {
8 |
9 | private static String geopointPrefix = "__GEOPOINT:";
10 |
11 | public JSONGeopointWrapper(GeoPoint geoPoint) {
12 | super(geoPoint.getLatitude(), geoPoint.getLongitude());
13 | }
14 |
15 | public static void setGeopointPrefix(String geopointPrefix) {
16 | JSONGeopointWrapper.geopointPrefix = geopointPrefix;
17 | }
18 |
19 | public static boolean isWrappedGeoPoint(Object value) {
20 |
21 |
22 | if (value instanceof String && ((String) value).startsWith(geopointPrefix)) {
23 | return true;
24 | }
25 |
26 | return false;
27 | }
28 |
29 | public static GeoPoint unwrapGeoPoint(Object value) {
30 | String stringValue = (String) value;
31 | int prefixLength = geopointPrefix.length();
32 | String latLng = stringValue.substring(prefixLength + 1);
33 | String[] tmp = latLng.split(",");
34 | return new GeoPoint(Double.parseDouble(tmp[0]), Double.parseDouble(tmp[1]));
35 | }
36 |
37 | @Override
38 | public String toString() {
39 | return this.geopointPrefix + this.getLatitude() + "," + this.getLongitude();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/android/uk/co/reallysmall/cordova/plugin/firestore/JSONHelper.java:
--------------------------------------------------------------------------------
1 | package uk.co.reallysmall.cordova.plugin.firestore;
2 |
3 | import com.google.firebase.firestore.DocumentReference;
4 |
5 | import com.google.firebase.Timestamp;
6 | import com.google.firebase.firestore.GeoPoint;
7 | import com.google.gson.Gson;
8 | import com.google.gson.reflect.TypeToken;
9 |
10 | import org.json.JSONArray;
11 | import org.json.JSONException;
12 | import org.json.JSONObject;
13 |
14 | import java.lang.reflect.Type;
15 | import java.util.ArrayList;
16 | import java.util.Date;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | public class JSONHelper {
21 |
22 | private static FirestorePlugin firestorePlugin;
23 |
24 | static void setPlugin(FirestorePlugin firestorePlugin) {
25 | JSONHelper.firestorePlugin = firestorePlugin;
26 | }
27 |
28 | static JSONObject toJSON(Map values) throws JSONException {
29 | JSONObject result = new JSONObject();
30 |
31 | for (Map.Entry entry : values.entrySet()) {
32 | Object value = entry.getValue();
33 | if (value instanceof Map) {
34 | value = toJSON((Map) value);
35 | ;
36 | } else if (value instanceof List) {
37 | value = toJSONArray((List) value);
38 | } else if (value instanceof Date) {
39 | value = new JSONDateWrapper((Date) value);
40 | } else if (value instanceof Timestamp) {
41 | value = new JSONTimestampWrapper((Timestamp) value);
42 | } else if (value instanceof GeoPoint) {
43 | value = new JSONGeopointWrapper((GeoPoint) value);
44 | } else if (value instanceof DocumentReference) {
45 | value = new JSONReferenceWrapper((DocumentReference) value);
46 | }
47 | result.put(entry.getKey(), value);
48 | }
49 | return result;
50 | }
51 |
52 | private static JSONArray toJSONArray(List values) throws JSONException {
53 | JSONArray result = new JSONArray();
54 |
55 | for (Object value : values) {
56 | if (value instanceof Map) {
57 | value = toJSON((Map) value);
58 | ;
59 | } else if (value instanceof List) {
60 | value = toJSONArray((List) value);
61 | } else if (value instanceof Date) {
62 | value = new JSONDateWrapper((Date) value);
63 | } else if (value instanceof Timestamp) {
64 | value = new JSONTimestampWrapper((Timestamp) value);
65 | } else if (value instanceof GeoPoint) {
66 | value = new JSONGeopointWrapper((GeoPoint) value);
67 | } else if (value instanceof DocumentReference) {
68 | value = new JSONReferenceWrapper((DocumentReference) value);
69 | }
70 | result.put(value);
71 | }
72 | return result;
73 | }
74 |
75 |
76 | public static Object fromJSON(Object value) {
77 | Object newValue;
78 |
79 | if (value instanceof String) {
80 | if (JSONGeopointWrapper.isWrappedGeoPoint(value)) {
81 | newValue = JSONGeopointWrapper.unwrapGeoPoint(value);
82 | } else if (JSONDateWrapper.isWrappedDate(value)) {
83 | newValue = JSONDateWrapper.unwrapDate(value);
84 | } else if (JSONTimestampWrapper.isWrappedTimestamp(value)) {
85 | newValue = JSONTimestampWrapper.unwrapTimestamp(value);
86 | } else if (JSONReferenceWrapper.isWrappedReference(value)) {
87 | newValue = JSONReferenceWrapper.unwrapReference(JSONHelper.firestorePlugin, value);
88 | } else if (FieldValueHelper.isWrappedFieldValue(value)) {
89 | newValue = FieldValueHelper.unwrapFieldValue(value);
90 | } else {
91 | newValue = value;
92 | }
93 | } else if (value instanceof Map) {
94 | newValue = toSettableMapInternal((Map