Source: client/firebase-database-client.js

  1. /*
  2. * Copyright 2023, TeamDev. All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Redistribution and use in source and/or binary forms, with or without
  11. * modification, must retain the above copyright notice and the following
  12. * disclaimer.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  15. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  16. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  17. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  18. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  19. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  20. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. "use strict";
  27. import {Subscription, Subject} from 'rxjs';
  28. import { ref, onValue,
  29. onChildAdded, onChildRemoved, onChildChanged, onChildMoved } from "firebase/database";
  30. /**
  31. * The client of a Firebase Realtime database.
  32. */
  33. export class FirebaseDatabaseClient {
  34. /**
  35. * Creates a new FirebaseDatabaseClient.
  36. *
  37. * @param {!firebase.database.Database} database a database of the initialized Firebase application
  38. */
  39. constructor(database) {
  40. this._database = database;
  41. }
  42. /**
  43. * Subscribes to the `child_added` events of the node under the given path.
  44. *
  45. * Each child's value is parsed as a JSON and dispatched to the given callback
  46. *
  47. * @param {!string} path the path to the watched node
  48. * @param {!Subject<Object>} dataSubject the subject receiving child values
  49. *
  50. * @return {Subscription} a Subscription that can be unsubscribed
  51. */
  52. onChildAdded(path, dataSubject) {
  53. return this._subscribeToChildEvent('child_added', path, dataSubject);
  54. }
  55. /**
  56. * Subscribes to the `child_changed` events of the node under the given path.
  57. *
  58. * Each child's value is parsed as a JSON and dispatched to the given callback
  59. *
  60. * @param {!string} path the path to the watched node
  61. * @param {!Subject<Object>} dataSubject the subject receiving child values
  62. *
  63. * @return {Subscription} a Subscription that can be unsubscribed
  64. */
  65. onChildChanged(path, dataSubject) {
  66. return this._subscribeToChildEvent('child_changed', path, dataSubject);
  67. }
  68. /**
  69. * Subscribes to the `child_removed` events of the node under the given path.
  70. *
  71. * Each child's value is parsed as a JSON and dispatched to the given callback
  72. *
  73. * @param {!string} path the path to the watched node
  74. * @param {!Subject<Object>} dataSubject the subject receiving child values
  75. *
  76. * @return {Subscription} a Subscription that can be unsubscribed
  77. */
  78. onChildRemoved(path, dataSubject) {
  79. return this._subscribeToChildEvent('child_removed', path, dataSubject);
  80. }
  81. _subscribeToChildEvent(childEvent, path, dataSubject) {
  82. const dbRef = ref(this._database, path);
  83. let valueCallback = response => {
  84. const msgJson = response.val();
  85. const message = JSON.parse(msgJson);
  86. dataSubject.next(message);
  87. };
  88. let offCallback = null;
  89. switch (childEvent) {
  90. case 'child_added':
  91. offCallback = onChildAdded(dbRef, valueCallback);
  92. break;
  93. case 'child_removed':
  94. offCallback = onChildRemoved(dbRef, valueCallback);
  95. break;
  96. case 'child_changed':
  97. offCallback = onChildChanged(dbRef, valueCallback);
  98. break;
  99. case 'child_moved':
  100. offCallback = onChildMoved(dbRef, valueCallback);
  101. break;
  102. default:
  103. throw new Error('Invalid child event: ' + childEvent);
  104. }
  105. return new Subscription(() => {
  106. offCallback?.();
  107. dataSubject.complete();
  108. });
  109. }
  110. /**
  111. * Gets an array of values from Firebase at the provided path.
  112. *
  113. * @param {!string} path the path to the node to get value from
  114. * @param {!consumerCallback<Object[]>} dataCallback a callback which is invoked with an array of
  115. * entities at path
  116. */
  117. getValues(path, dataCallback) {
  118. const dbRef = ref(this._database, path);
  119. onValue(dbRef, response => {
  120. const data = response.val(); // an Object mapping Firebase ids to objects is returned
  121. if (data == null) {
  122. return dataCallback([]);
  123. }
  124. const objectStrings = Object.values(data);
  125. const items = objectStrings.map(item => JSON.parse(item));
  126. dataCallback(items);
  127. }, {
  128. onlyOnce: true
  129. })
  130. }
  131. }