const EventEmitter = require("events") const fetch = require("isomorphic-fetch") const config = require("../config") const compressQuery = require("graphql-query-compress") /** * Run a GraphQL query against the rpdata backend * * @param {string} query The query to run * @param {{[x:string]: any}} variables * @param {{operationName:string, token: string, permissions: string[]}} options */ function query(query, variables = {}, options = {}) { return fetch(config.graphqlEndpoint, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": options.token ? `Bearer ${options.token}` : null, //"X-Permissions": options.permissions ? options.permissions.join(",") : null, }, body: JSON.stringify({query: query.length > 256 ? compressQuery(query || "") : query, variables, operationName: options.operationName}), credentials: "include", }).then(res => { return res.json() }).then(json => { if (json.errors != null && json.errors.length > 0) { return Promise.reject(json.errors) } return json.data }) } /** * * * @param {string} query GQL Query * @param {Object} variables Variables * @param {{token?:string,permissions?:string[]}} options */ function queryForm(query, variables = {}, options = {}) { const map = {} const files = [] function findFiles(prefix, data) { for (const key in data) { if (!data.hasOwnProperty(key)) { continue } const value = data[key] if (value instanceof File) { map[files.length] = [`${prefix}.${key}`] files.push(value) } else if (typeof(value) === 'object') { data[key] = findFiles(`${prefix}.${key}`, Object.assign({}, value)) } } return data } const operations = { query: query, variables: findFiles("variables", Object.assign({}, variables)), } const form = new FormData() form.append("operations", JSON.stringify(operations)) form.append("map", JSON.stringify(map)) for (let i = 0; i < files.length; ++i) { form.append(i.toString(), files[i]) } console.log(form) return fetch(config.graphqlEndpoint, { method: "POST", headers: { "Authorization": options.token ? `Bearer ${options.token}` : null, "X-Permissions": options.permissions ? options.permissions.join(",") : null, }, body: form, credentials: "include", }).then(res => { return res.json() }).then(json => { if (json.errors != null && json.errors.length > 0) { return Promise.reject(json.errors) } return json.data }) } class Subscription extends EventEmitter { constructor(query, variables) { super() this.query = query this.variables = Object.assign({}, variables) this.websocket = null this.verboseLogging = false } connect() { const baseUrl = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host let id = 1 this.websocket = new WebSocket(baseUrl + config.graphqlEndpoint, "graphql-ws") this.websocket.onopen = () => { this.websocket.send(JSON.stringify({type: "connection_init", payload: {}})); this.websocket.send(JSON.stringify({id: (id++).toFixed(0), type: "start", payload: { query: this.query, variables: this.variables, extensions: {}, }})); console.log("WS Open") } this.websocket.onmessage = (ev) => { let data = {} try { data = JSON.parse(ev.data) } catch (err) { console.warn("Error", err, ev) return } if (this.verboseLogging) { console.log("WS Data:", data) } switch (data.type) { case "connection_ack": { this.emit("connect") break; } case "connection_error": { console.warn("WS Connection Error", data.payload.message) this.websocket.close() this.emit("error", new Error(data.payload.message)) break; } case "data": { if (data.payload.errors != null && data.payload.errors.length > 0) { this.emit("error", data.payload.errors) } else { this.emit("data", data.payload.data) } break; } } } this.websocket.onerror = (err) => { console.warn("WS Error", err) } this.websocket.onclose = () => { this.emit("disconnect") } } disconnect() { if (this.websocket != null) { this.websocket.close(); } } } module.exports = { query, queryForm, Subscription }