Skip to content

Commit c8e1a0f

Browse files
authored
STITCH-2499 Add support for 'watch' in React Native (#294)
1 parent bd07a3d commit c8e1a0f

File tree

12 files changed

+429
-14
lines changed

12 files changed

+429
-14
lines changed

packages/core/testutils/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
},
5353
"devDependencies": {
5454
"cross-fetch": "^2.2.3",
55+
"eventsource": "^1.0.7",
5556
"prettier": "^1.13.5",
5657
"tslint": "^5.10.0",
5758
"tslint-config-prettier": "^1.13.0",

packages/core/testutils/src/JestFetchStreamTransport.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import fetch from "cross-fetch";
18+
import EventSource from "eventsource";
1819
import {
1920
BasicRequest,
2021
ContentTypes,

packages/react-native/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"license": "Apache-2.0",
4848
"dependencies": {
4949
"bson": "4.0.2",
50-
"mongodb-stitch-core-sdk": "^4.8.0"
50+
"mongodb-stitch-core-sdk": "^4.8.0",
51+
"rn-eventsource": "^1.0.0"
5152
},
5253
"devDependencies": {
5354
"prettier": "^1.13.5",

packages/react-native/core/src/core/Stitch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from "mongodb-stitch-core-sdk";
2020

2121
import RNAsyncStorage from "./internal/common/RNAsyncStorage";
22-
import RNFetchTransport from "./internal/net/RNFetchTransport";
22+
import RNFetchStreamTransport from "./internal/net/RNFetchStreamTransport";
2323
import StitchAppClientImpl from "./internal/StitchAppClientImpl";
2424
import StitchAppClient from "./StitchAppClient";
2525

@@ -139,7 +139,7 @@ export default class Stitch {
139139
builder.withStorage(new RNAsyncStorage(clientAppId));
140140
}
141141
if (builder.transport === undefined) {
142-
builder.withTransport(new RNFetchTransport());
142+
builder.withTransport(new RNFetchStreamTransport());
143143
}
144144
if (builder.baseUrl === undefined || builder.baseUrl === "") {
145145
builder.withBaseUrl(DEFAULT_BASE_URL);

packages/react-native/core/src/core/internal/common/RNAsyncStorage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { Storage } from "mongodb-stitch-core-sdk";
18-
import { AsyncStorage } from "react-native"
18+
import { AsyncStorage } from "react-native";
1919

2020
const stitchPrefixKey = "__stitch.client";
2121

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/**
2+
* Copyright 2018-present MongoDB, Inc.
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+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import {
18+
BaseEventStream,
19+
Event,
20+
StitchClientError,
21+
StitchClientErrorCode,
22+
StitchEvent
23+
} from "mongodb-stitch-core-sdk";
24+
import EventSource from "rn-eventsource";
25+
26+
/** @hidden */
27+
export default class EventSourceEventStream extends BaseEventStream<EventSourceEventStream> {
28+
private evtSrc: EventSource;
29+
private readonly onOpenError: (error: Error) => void;
30+
private openedOnce: boolean;
31+
32+
constructor(
33+
evtSrc: EventSource,
34+
onOpen: (stream: EventSourceEventStream) => void,
35+
onOpenError: (error: Error) => void,
36+
reconnecter?: () => Promise<EventSourceEventStream>
37+
) {
38+
super(reconnecter);
39+
this.evtSrc = evtSrc;
40+
this.onOpenError = onOpenError;
41+
this.openedOnce = false;
42+
43+
this.evtSrc.onopen = e => {
44+
onOpen(this);
45+
this.openedOnce = true;
46+
};
47+
this.reset();
48+
}
49+
50+
public open(): void {
51+
if (this.closed) {
52+
throw new StitchClientError(StitchClientErrorCode.StreamClosed);
53+
}
54+
}
55+
56+
public afterClose(): void {
57+
this.evtSrc.close();
58+
}
59+
60+
protected onReconnect(next: EventSourceEventStream) {
61+
this.evtSrc = next.evtSrc;
62+
this.reset();
63+
this.events = next.events.concat(this.events);
64+
}
65+
66+
private reset(): void {
67+
this.evtSrc.onmessage = e => {
68+
this.events.push(new Event(Event.MESSAGE_EVENT, e.data));
69+
this.poll();
70+
};
71+
this.evtSrc.onerror = e => {
72+
if (e.data !== undefined) {
73+
this.lastErr = e.data;
74+
this.events.push(new Event(StitchEvent.ERROR_EVENT_NAME, this.lastErr!));
75+
this.close();
76+
this.poll();
77+
return;
78+
}
79+
if (!this.openedOnce) {
80+
this.close();
81+
this.onOpenError(new Error("event source failed to open and will not reconnect; check network log for more details"));
82+
return;
83+
}
84+
this.evtSrc.close();
85+
this.reconnect();
86+
}
87+
}
88+
}

packages/react-native/core/src/core/internal/net/RNFetchTransport.ts renamed to packages/react-native/core/src/core/internal/net/RNFetchStreamTransport.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,17 @@ import {
1818
BasicRequest,
1919
ContentTypes,
2020
EventStream,
21+
handleRequestError,
2122
Headers,
2223
Response,
23-
StitchClientError,
24-
StitchClientErrorCode,
25-
Transport
24+
Transport,
2625
} from "mongodb-stitch-core-sdk";
27-
26+
27+
import EventSource from "rn-eventsource";
28+
import EventSourceEventStream from "./EventSourceEventStream";
29+
2830
/** @hidden */
29-
export default class RNFetchTransport implements Transport {
31+
export default class RNFetchStreamTransport implements Transport {
3032
public roundTrip(request: BasicRequest): Promise<Response> {
3133
const responsePromise = fetch(request.url, {
3234
body: request.body,
@@ -51,6 +53,37 @@ import {
5153
}
5254

5355
public stream(request: BasicRequest, open = true, retryRequest?: () => Promise<EventStream>): Promise<EventStream> {
54-
throw new StitchClientError(StitchClientErrorCode.StreamingNotSupported);
56+
const reqHeaders = { ...request.headers };
57+
reqHeaders[Headers.ACCEPT] = ContentTypes.TEXT_EVENT_STREAM;
58+
reqHeaders[Headers.CONTENT_TYPE] = ContentTypes.TEXT_EVENT_STREAM;
59+
60+
// Verify we can start a request with current params and potentially
61+
// Force ourselves to refresh a token.
62+
return fetch(request.url + "&stitch_validate=true", {
63+
body: request.body,
64+
headers: reqHeaders,
65+
method: request.method,
66+
mode: 'cors'
67+
}).then(response => {
68+
const respHeaders: { [key: string]: string } = {};
69+
response.headers.forEach((value, key) => {
70+
respHeaders[key] = value;
71+
});
72+
if (response.status < 200 || response.status >= 300) {
73+
return response.text()
74+
.then(body => handleRequestError(new Response(respHeaders, response.status, body)));
75+
}
76+
77+
return new Promise<EventStream>((resolve, reject) =>
78+
new EventSourceEventStream(
79+
new EventSource(request.url),
80+
stream => resolve(stream),
81+
error => reject(error),
82+
retryRequest ?
83+
() => retryRequest().then(es => es as EventSourceEventStream)
84+
: undefined
85+
)
86+
);
87+
});
5588
}
5689
}

packages/react-native/core/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import StitchAuth from "./core/auth/StitchAuth";
5959
import StitchAuthListener from "./core/auth/StitchAuthListener";
6060
import StitchUser from "./core/auth/StitchUser";
6161
import RNAsyncStorage from "./core/internal/common/RNAsyncStorage";
62-
import RNFetchTransport from "./core/internal/net/RNFetchTransport";
62+
import RNFetchStreamTransport from "./core/internal/net/RNFetchStreamTransport";
6363
import Stitch from "./core/Stitch";
6464
import StitchAppClient from "./core/StitchAppClient";
6565
import NamedServiceClientFactory from "./services/internal/NamedServiceClientFactory";
@@ -82,7 +82,7 @@ export {
8282
GoogleCredential,
8383
MemoryStorage,
8484
NamedServiceClientFactory,
85-
RNFetchTransport,
85+
RNFetchStreamTransport,
8686
RNAsyncStorage,
8787
ServerApiKeyAuthProvider,
8888
ServerApiKeyCredential,

0 commit comments

Comments
 (0)