UNPKG

9.85 kBTypeScriptView Raw
1declare global {
2 interface Window {
3 __APOLLO_STATE__: any;
4 __GRAPHQL__: any;
5 __SCHEMA__: any;
6 }
7}
8
9import * as React from 'react';
10
11import { InMemoryCache } from "apollo-cache-inmemory";
12import { createHttpLink } from 'apollo-link-http';
13import {ApolloProvider, ApolloConsumer, getDataFromTree} from "react-apollo";
14
15import ApolloClient from 'apollo-client';
16//import { ApolloLink } from 'apollo-link';
17
18import { SchemaLink } from 'apollo-link-schema';
19
20//import fetch from 'node-fetch';
21require('es6-promise').polyfill();
22import 'isomorphic-fetch';
23
24
25/**
26 * we MUST NOT IMPORT CONTEXTs directly, but require them at time of use generally from Infrastructure-Components
27 * because this then resolves to node_modules
28 */
29
30//import AttachDataLayer from './attach-data-layer';
31
32import Types from '../types';
33import { extractObject, INFRASTRUCTURE_MODES, loadConfigurationFromModule } from '../libs/loader';
34
35
36/**
37 *
38 */
39export const createServerSideClient = (link) => new ApolloClient({
40 //ssrMode: true,
41 // Remember that this is the interface the SSR server will use to connect to the
42 // API server, so we need to ensure it isn't firewalled, etc
43 link:link,
44 cache: new InMemoryCache(),
45
46});
47
48
49/**
50 * Puts the preloaded state in a string that can be put into a <script>-tag
51 * Also puts the path to the GraphQL-Endpoint there
52 *
53 * @param preloadedState the state
54 */
55
56// here, we can also add stringified (json) data about the dataLayer, e.g. Schema
57// it works without ... window.__SCHEMA__ = \`${schema}\`
58export const importEnvironmentVariables = (preloadedState, url) => {
59 //console.log("graphql-url: ", url);
60 return `window.__APOLLO_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\\u003c')};
61window.__GRAPHQL__ = "${url}"`;
62
63}
64
65
66
67/**
68 * Function to be used on the client side to connect the client app with the dehydrated state from the server
69 * to be rehydrated on the client
70 *
71 * Note: Queries and Mutations work with POST-requests, if you want query requests to work with GET,
72 * set `useGETForQueries` to true. Mutations always use POST
73 *
74 * @param app the ReactApp to connect with the DataLayer
75 */
76export const hydrateFromDataLayer = (app, dataLayer) => {
77
78 const AttachDataLayer = require("infrastructure-components").AttachDataLayer;
79 //console.log("hydration, dataLayer: ", dataLayer);
80
81 var preloadedState = {};
82 if (typeof window != 'undefined' && window.__APOLLO_STATE__) {
83 preloadedState = window.__APOLLO_STATE__;
84 delete window.__APOLLO_STATE__;
85 }
86
87 //console.log("uri: ", window.__GRAPHQL__);
88
89 const client = new ApolloClient({
90 cache: new InMemoryCache().restore(preloadedState),
91 link: createHttpLink({
92 uri: window.__GRAPHQL__,
93 credentials: 'include',
94 })
95 });
96
97 //console.log("local client: ", client);
98
99 return <ApolloProvider client={ client } >
100 <ApolloConsumer>
101 {client => <AttachDataLayer apolloClient={client} dataLayer={dataLayer}>{app}</AttachDataLayer>}
102 </ApolloConsumer>
103
104 </ApolloProvider>
105};
106
107/**
108 * function to be used to provide the data-layer to a single-page-app-client (esp. SOA!)
109 */
110export const renderFromDataLayer = (app, dataLayer) => {
111 const AttachDataLayer = require("infrastructure-components").AttachDataLayer;
112 //console.log("connect, dataLayer: ", dataLayer);
113
114
115
116 //console.log("uri: ", window.__GRAPHQL__);
117
118 const client = new ApolloClient({
119 cache: new InMemoryCache(),
120 link: createHttpLink(Object.assign({
121 uri: window.__GRAPHQL__,
122
123 /*credentials: 'include',
124 headers: {
125 "Access-Control-Allow-Origin": "*",
126 "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
127 'Access-Control-Allow-Headers': 'application/json, Content-Type,token'
128 }*/
129 },
130 /*window.__GRAPHQL__.indexOf("localhost") > 0 ? {fetchOptions: {
131 mode: "no-cors",
132 }} : {}*/
133 ))
134 });
135
136 //console.log("local client: ", client);
137
138
139 return <ApolloProvider client={ client } >
140 <ApolloConsumer>
141 {client => <AttachDataLayer apolloClient={client} dataLayer={dataLayer}>{app}</AttachDataLayer>}
142 </ApolloConsumer>
143
144 </ApolloProvider>
145
146}
147
148export const createApolloClient = (dataLayer, graphqlUrl, request) => {
149 //console.log("STAGE_PATH: ", process.env.STAGE_PATH);
150 //console.log("DOMAIN_ENABLED: ", process.env.DOMAIN_ENABLED);
151
152 // TODO take these variables, esp. GraphQl-Path from the component rather than from the env-variables,
153 // because, there might be more than one DataLayers!
154 // when we have a valid stage_path or a domain, e.g. https://xxxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/dev/query',
155 /*const graphqlUrl = (
156 process.env.STAGE_PATH !== undefined && process.env.STAGE_PATH !== "undefined" && process.env.STAGE_PATH.length > 0
157 ) || process.env.DOMAIN_ENABLED === "true" ?
158 // then use the path
159 (process.env.DOMAIN_ENABLED !== undefined && process.env.DOMAIN_ENABLED === "true" &&
160 process.env.DOMAIN_NAME !== undefined && process.env.DOMAIN_NAME !== undefined ?
161 "https://"+process.env.DOMAIN_NAME : process.env.DOMAIN_URL) + "/"+process.env.GRAPHQL_PATH :
162 // else mock the endpoint - use the dev-endpoint
163 "https://yfse1b9v0m.execute-api.eu-west-1.amazonaws.com/dev/query"; // undefined; //
164 //
165 // TODO set undefined or a Mock or the Dev-Address here
166
167 */
168
169
170 //console.log("graphqlUrl: ", graphqlUrl);
171 //console.log("schema: ", schema);
172
173 const client = new ApolloClient({
174 ssrMode: true,
175
176 //ssrForceFetchDelay: 100,
177 cache: new InMemoryCache(),
178 /* instead of the createHttpLink, we use SchemaLink({ schema }) to void network traffic, for both endpoints
179 * implements [[UseSchemaLinkSpec]]
180 *
181 */
182 link: new SchemaLink({
183 schema: dataLayer.getSchema(true),
184 context: {
185 // when we have a userId, put it into the context so thatwe have it through ssr, too.
186 userId: require("infrastructure-components").getUserId(request)
187 }
188 })
189 /*link: createHttpLink({
190 uri: graphqlUrl,
191 fetch: awsGraphqlFetch,
192 credentials: 'include',
193 }),*/
194
195 });
196
197 //console.log("client: ", client);
198 return client;
199
200
201}
202
203export const getGraphqlUrl = (isOffline) => {
204 // does not work offline, but does not need to!
205 //console.log("Domain: ", process.env.DOMAIN_URL)
206
207 // TODO this should not be hard-coded - see SOA plugin!
208 return isOffline? "http://localhost:3000/query" : process.env.DOMAIN_URL + "/"+process.env.GRAPHQL_PATH;
209}
210
211
212/**
213 * Function to be used on server side that connects a ReactApp (jsx) with a GraphQL-Layer hosted on AWS Lambda/DynamoDb
214 * Creates the server side store
215 *
216 * Fetch-library is required , see [this](https://github.com/apollographql/apollo-client/issues/3578)
217 * see [fetch polyfill](https://www.apollographql.com/docs/link/links/http.html#options)
218 *
219 * @param app the ReactApp to connect with the DataLayer
220 * @schema specifies the schema to connect the store with, if undefined: use the uri (via network). see [[UseSchemaLinkSpec]]
221 */
222export const connectWithDataLayer = (dataLayerId, request, isOffline) => async (app) => {
223
224 /**
225 * we MUST NOT IMPORT CONTEXTs directly, but require them at time of use generally from Infrastructure-Components
226 * because this then resolves to node_modules
227 */
228 const AttachDataLayer = require("infrastructure-components").AttachDataLayer;
229
230 //console.log("AttachDataLayer: ", AttachDataLayer)
231
232 //console.log("connectWithDataLayer: ", dataLayerId);
233 // load the IsomorphicComponent
234 // we must load it directly from the module here, to enable the aliad of the config_file_path
235 const isoConfig = loadConfigurationFromModule(require('__CONFIG_FILE_PATH__'), INFRASTRUCTURE_MODES.RUNTIME);
236
237 // with a clientApp, we can check whether it is wrapped into a datalayer
238 const dataLayer = extractObject(
239 isoConfig,
240 Types.INFRASTRUCTURE_TYPE_COMPONENT,
241 dataLayerId
242 );
243
244 if (isOffline) {
245 //console.log("connectWithDataLayer - setOffline!")
246 dataLayer.setOffline(true);
247 }
248
249
250 //console.log("dataLayer: ", dataLayer);
251
252 return new Promise<any>(async (resolve, reject) => {
253 const awsGraphqlFetch = (uri, options) => {
254 //console.log("fetch: ",uri, options);
255 //options.headers["Authorization"] = token;
256 return fetch(uri, options);
257 };
258
259 const graphqlUrl = getGraphqlUrl(isOffline); // "https://yfse1b9v0m.execute-api.eu-west-1.amazonaws.com/dev/query";// process.env.DOMAIN_URL + "/"+process.env.GRAPHQL_PATH;
260
261 const client = createApolloClient(dataLayer, graphqlUrl, request);
262 dataLayer.setClient(client);
263
264 const connectedApp = <ApolloProvider client={client}>
265 <ApolloConsumer>
266 {client => <AttachDataLayer apolloClient={client} dataLayer={dataLayer}>{app}</AttachDataLayer>}
267 </ApolloConsumer>
268 </ApolloProvider>
269
270 //console.log("connectedApp: ", connectedApp);
271 try {
272 //.catch((err) => console.log("err: ", err))
273 await getDataFromTree(connectedApp).then(() => resolve({connectedApp: connectedApp, getState: () => {
274 //console.log("time to resolve");
275 const data = client.extract();
276 //console.log("data: ", data);
277 return importEnvironmentVariables(data, graphqlUrl.trim())
278 }}));
279 } catch (error) {
280 console.log("error: ", error);
281 }
282
283 });
284}
285
\No newline at end of file