Source: client/spine.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 {FirebaseClientFactory} from './firebase-client';
  28. import {CustomClientFactory} from './client-factory';
  29. import {DirectClientFactory} from "./direct-client";
  30. import {CompositeClient} from "./composite-client";
  31. import KnownTypes from "./known-types";
  32. import TypeParsers from "./parser/type-parsers";
  33. /**
  34. * @typedef {Object} CompositeClientOptions is a configuration of a composite client, which allows
  35. * different client implementations to be used for different client requests.
  36. *
  37. * @property {!Array<Object>} protoIndexFiles
  38. * the list of the `index.js` files generated by {@link https://github.com/SpineEventEngine/base/tree/master/tools/proto-js-plugin the Protobuf plugin for JS}
  39. * @property {ClientOptions} forQueries
  40. * options of the client used for queries
  41. * @property {ClientOptions} forSubscriptions
  42. * options of the client used for subscriptions
  43. * @property {ClientOptions} forCommands
  44. * options of the client used for commands
  45. */
  46. /**
  47. * The main entry point of the `spine-web` JS library. Serves for initialization
  48. * of the `Client` instances to interact with Spine-based backend.
  49. *
  50. * To initialize a new instance of client that uses Firebase as a storage do the following:
  51. * ```
  52. * import * as protobufs from './proto/index.js';
  53. * import * as spineWeb from 'spine-web';
  54. *
  55. * const firebaseApp = Firebase.initializeApp({...Firebase options});
  56. *
  57. * // The backend client will receive updates of the current actor through this instance
  58. * const actorProvider = new ActorProvider();
  59. *
  60. * const client = spineWeb.init({
  61. * protoIndexFiles: [protobufs],
  62. * endpointUrl: 'http://example.appspot.com',
  63. * firebaseDatabase: firebaseApp.database(),
  64. * actorProvider: actorProvider
  65. * });
  66. * ```
  67. *
  68. * To substitute a custom implementation of `Client` for tests do the following:
  69. * ```
  70. * // An instance of class extending `spineWeb.Client`
  71. * const mockClientImpl = new MockClient();
  72. *
  73. * const mockClient = spineWeb.init({
  74. * protoIndexFiles: [protobufs],
  75. * implementation: mockClientImpl
  76. * });
  77. * ```
  78. * Note, when using of custom `Client` implementation protobuf index files
  79. * registration is still required.
  80. *
  81. * @param {ClientOptions|CompositeClientOptions} options
  82. * @return {Client}
  83. */
  84. export function init(options) {
  85. _registerTypes(...options.protoIndexFiles);
  86. const compositeClient = _initCompositeClient(options);
  87. return compositeClient !== null ? compositeClient : _initSimpleClient(options);
  88. }
  89. function _initCompositeClient(options) {
  90. const forQueries = options.forQueries;
  91. const forSubscriptions = options.forSubscriptions;
  92. const forCommands = options.forCommands;
  93. if (!!forQueries || !!forSubscriptions || !!forCommands) {
  94. if (!(!!forQueries && !!forSubscriptions && !!forCommands)) {
  95. throw Error("All of `forQueries`, `forSubscriptions`, and `forCommands` must be defined.");
  96. }
  97. } else {
  98. return null;
  99. }
  100. const querying = _selectFactory(forQueries).createQuerying(forQueries);
  101. const subscribing = _selectFactory(forSubscriptions).createSubscribing(forSubscriptions);
  102. const commanding = _selectFactory(forCommands).createCommanding(forCommands);
  103. return new CompositeClient(querying, subscribing, commanding);
  104. }
  105. function _initSimpleClient(options) {
  106. const factory = _selectFactory(options);
  107. return factory.createClient(options);
  108. }
  109. function _selectFactory(options) {
  110. let clientFactory;
  111. if (!!options.firebaseDatabase) {
  112. clientFactory = FirebaseClientFactory;
  113. } else if (!!options.implementation) {
  114. clientFactory = CustomClientFactory;
  115. } else {
  116. clientFactory = DirectClientFactory;
  117. }
  118. return clientFactory;
  119. }
  120. /**
  121. * Registers all Protobuf types provided by the specified modules.
  122. *
  123. * After the registration, the types can be used and parsed correctly.
  124. *
  125. * @param protoIndexFiles the index.js files generated by
  126. * {@link https://github.com/SpineEventEngine/base/tree/master/tools/proto-js-plugin the Protobuf plugin for JS}
  127. * @private
  128. */
  129. function _registerTypes(...protoIndexFiles) {
  130. for (let indexFile of protoIndexFiles) {
  131. for (let [typeUrl, type] of indexFile.types) {
  132. KnownTypes.register(type, typeUrl);
  133. }
  134. for (let [typeUrl, parserType] of indexFile.parsers) {
  135. TypeParsers.register(new parserType(), typeUrl);
  136. }
  137. }
  138. }