1 | declare global {
|
2 | interface Window {
|
3 | __APOLLO_STATE__: any;
|
4 | __GRAPHQL__: any;
|
5 | __SCHEMA__: any;
|
6 | }
|
7 | }
|
8 |
|
9 | import * as React from 'react';
|
10 |
|
11 | import { InMemoryCache } from "apollo-cache-inmemory";
|
12 | import { createHttpLink } from 'apollo-link-http';
|
13 | import {ApolloProvider, ApolloConsumer, getDataFromTree} from "react-apollo";
|
14 |
|
15 | import ApolloClient from 'apollo-client';
|
16 | //import { ApolloLink } from 'apollo-link';
|
17 |
|
18 | import { SchemaLink } from 'apollo-link-schema';
|
19 |
|
20 | //import fetch from 'node-fetch';
|
21 | require('es6-promise').polyfill();
|
22 | import '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 |
|
32 | import Types from '../types';
|
33 | import { extractObject, INFRASTRUCTURE_MODES, loadConfigurationFromModule } from '../libs/loader';
|
34 |
|
35 |
|
36 | /**
|
37 | *
|
38 | */
|
39 | export 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}\`
|
58 | export const importEnvironmentVariables = (preloadedState, url) => {
|
59 | //console.log("graphql-url: ", url);
|
60 | return `window.__APOLLO_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\\u003c')};
|
61 | window.__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 | */
|
76 | export 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 | */
|
110 | export 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 |
|
148 | export 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 |
|
203 | export 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 | */
|
222 | export 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 |