├── src ├── config.js ├── index.css ├── setupProxy.js ├── App.css ├── components │ ├── Error.js │ ├── Loading.js │ ├── View.js │ ├── AuthWizard.js │ ├── auth │ │ ├── Auth.js │ │ ├── Register.js │ │ ├── Login.js │ │ ├── LoginForm.js │ │ └── RegisterForm.js │ ├── DeleteDialog.js │ ├── ConfigWizard.js │ ├── Nav.js │ └── Instance.js ├── index.js ├── WithTheme.js ├── lib │ ├── authWindow.js │ └── configWindow.js ├── Router.js ├── api │ ├── me.js │ └── solutions.js ├── views │ ├── demo.css │ ├── Account.js │ ├── SolutionsMine.js │ ├── SolutionsDiscover.js │ ├── Demo.js │ └── Authentications.js ├── logo.svg └── registerServiceWorker.js ├── public ├── app.ico ├── app.icns ├── favicon.ico ├── favicon-32.png ├── favicon-57.png ├── favicon-72.png ├── favicon-96.png ├── favicon-120.png ├── favicon-128.png ├── favicon-144.png ├── favicon-152.png ├── favicon-195.png ├── favicon-228.png ├── manifest.json └── index.html ├── .images └── getting-token.png ├── Dockerfile ├── .graphqlconfig ├── server ├── logging.js ├── server.js ├── domain │ ├── login.js │ └── registration.js ├── db.js ├── gqlclient.js ├── configuration.js ├── auth.js ├── graphql.js └── api.js ├── .gitignore ├── graphql.config.json ├── package.json └── README.md /src/config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | username: 'a', 3 | password: 'a', 4 | }; 5 | -------------------------------------------------------------------------------- /public/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/app.ico -------------------------------------------------------------------------------- /public/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/app.icns -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-32.png -------------------------------------------------------------------------------- /public/favicon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-57.png -------------------------------------------------------------------------------- /public/favicon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-72.png -------------------------------------------------------------------------------- /public/favicon-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-96.png -------------------------------------------------------------------------------- /public/favicon-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-120.png -------------------------------------------------------------------------------- /public/favicon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-128.png -------------------------------------------------------------------------------- /public/favicon-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-144.png -------------------------------------------------------------------------------- /public/favicon-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-152.png -------------------------------------------------------------------------------- /public/favicon-195.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-195.png -------------------------------------------------------------------------------- /public/favicon-228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/public/favicon-228.png -------------------------------------------------------------------------------- /.images/getting-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trayio/embedded-edition-sample-app/HEAD/.images/getting-token.png -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:6-alpine 2 | 3 | COPY ./ /oem 4 | WORKDIR /oem 5 | 6 | RUN npm install 7 | 8 | ENTRYPOINT ["/usr/local/bin/npm"] 9 | CMD ["start"] 10 | 11 | -------------------------------------------------------------------------------- /src/setupProxy.js: -------------------------------------------------------------------------------- 1 | const proxy = require('http-proxy-middleware'); 2 | 3 | module.exports = function(app) { 4 | app.use(proxy('/api', { target: 'http://localhost:3001' })); 5 | }; 6 | -------------------------------------------------------------------------------- /.graphqlconfig: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "TrayOEM": { 4 | "schemaPath": "graphql.schema.json", 5 | "extensions": { 6 | "endpoints": { 7 | "default": "https://staging.tray.io/graphql" 8 | } 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /server/logging.js: -------------------------------------------------------------------------------- 1 | 2 | export const log = ({object, message}) => { 3 | if (!process.env.quiet) { 4 | console.log('------------------------------'); 5 | if (message) { 6 | console.log(message); 7 | } 8 | if (object) { 9 | console.log(JSON.stringify(object, null, 4)); 10 | } 11 | console.log('------------------------------'); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | font-family: "Roboto", "Helvetica", "Arial", sans-serif; 4 | } 5 | 6 | .App-logo { 7 | animation: App-logo-spin infinite 20s linear; 8 | height: 80px; 9 | } 10 | 11 | .App-header { 12 | background-color: #222; 13 | height: 150px; 14 | padding: 20px; 15 | color: white; 16 | } 17 | 18 | .App-title { 19 | font-size: 1.5em; 20 | } 21 | 22 | .App-intro { 23 | font-size: large; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { transform: rotate(0deg); } 28 | to { transform: rotate(360deg); } 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {withTheme} from '@material-ui/core/styles'; 3 | 4 | const styles = { 5 | container: { 6 | width: "100%", 7 | height: "100%", 8 | display: "flex", 9 | alignItems: "center", 10 | justifyContent: "center", 11 | color: "crimson", 12 | padding: '0 20px', 13 | } 14 | }; 15 | 16 | const Error = ({msg}) => ( 17 |
18 |
{JSON.stringify(msg, null, 4)}
19 |
20 | ); 21 | 22 | export default withTheme()(Error); -------------------------------------------------------------------------------- /src/components/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CircularProgress from '@material-ui/core/CircularProgress'; 3 | 4 | class Loading extends React.Component { 5 | render() { 6 | const styles = { 7 | container: { 8 | width: "100%", 9 | height: "100%", 10 | display: "flex", 11 | alignItems: "center", 12 | justifyContent: "center", 13 | } 14 | }; 15 | 16 | const spinner = ( 17 |
18 | 19 |
20 | ); 21 | 22 | return this.props.loading ? spinner : this.props.children; 23 | } 24 | } 25 | 26 | export default Loading; 27 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter } from 'react-router-dom' 4 | import './index.css'; 5 | import App from './Router'; 6 | import registerServiceWorker from './registerServiceWorker'; 7 | import blue from '@material-ui/core/colors/blue'; 8 | import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; 9 | 10 | const theme = createMuiTheme({ 11 | palette: { 12 | type: 'light', 13 | primary: blue, 14 | }, 15 | }); 16 | 17 | ReactDOM.render(( 18 |
19 | 20 | 21 | 22 | 23 | 24 |
25 | ), document.getElementById('root')); 26 | 27 | registerServiceWorker(); 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | # See https://help.github.com/ignore-files/ for more about ignoring files. 23 | 24 | # dependencies 25 | /node_modules 26 | 27 | # testing 28 | /coverage 29 | 30 | # production 31 | /build 32 | 33 | # misc 34 | .DS_Store 35 | .env.local 36 | .env.development.local 37 | .env.test.local 38 | .env.production.local 39 | 40 | npm-debug.log* 41 | yarn-debug.log* 42 | yarn-error.log* 43 | .idea/OEMReactSample.iml 44 | .idea/inspectionProfiles/ 45 | .idea/misc.xml 46 | .idea/modules.xml 47 | .idea/workspace.xml 48 | .idea/vcs.xml 49 | .idea/ 50 | 51 | .env 52 | .token 53 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | let bodyParser = require('body-parser'); 5 | 6 | // support json encoded bodies 7 | app.use(bodyParser.json()); 8 | // support encoded bodies 9 | app.use(bodyParser.urlencoded({extended: true})); 10 | 11 | // Set CORS headers 12 | app.use(function (req, res, next) { 13 | res.header("Access-Control-Allow-Origin", ["localhost"]); 14 | res.header("Access-Control-Allow-Credentials", true); 15 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 16 | next(); 17 | }); 18 | 19 | // Configure Express application: 20 | app.use(require('morgan')('tiny')); 21 | 22 | require('./configuration').setEnvironment(); 23 | 24 | // Authentication and Authorization Middleware: 25 | require('./auth')(app); 26 | 27 | // Setup API router: 28 | require('./api')(app); 29 | 30 | app.listen(process.env.PORT || 3001, () => { 31 | console.log(`Express started on port ${process.env.PORT || 3001} with Graphql endpoint ${process.env.TRAY_ENDPOINT}`); 32 | }); 33 | -------------------------------------------------------------------------------- /server/domain/login.js: -------------------------------------------------------------------------------- 1 | /** @module domain/login */ 2 | 3 | import {get} from 'lodash'; 4 | import {mutations} from '../graphql'; 5 | import {retrieveUserFromMockDB} from '../db'; 6 | 7 | /** 8 | * Attempt to retrieve a user from the DB: 9 | * @param {Request} 10 | * @return {User | undefined} 11 | */ 12 | export const attemptLogin = req => { 13 | const user = retrieveUserFromMockDB(req.body); 14 | 15 | if (user) { 16 | req.session.user = user; 17 | req.session.admin = true; 18 | } 19 | 20 | return user; 21 | }; 22 | 23 | /** 24 | * Attempt to generate access token for a given user: 25 | * @param {Request} 26 | * @param {Response} 27 | * @param {User} 28 | * @return {Promise} Promise that wraps authorization mutation. 29 | */ 30 | export const generateUserAccessToken = (req, res, user) => 31 | mutations.authorize(user.trayId) 32 | .then(authorizeResponse => { 33 | req.session.token = get( 34 | authorizeResponse, 35 | 'data.authorize.accessToken' 36 | ); 37 | 38 | return authorizeResponse; 39 | }); 40 | -------------------------------------------------------------------------------- /src/WithTheme.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Typography from '@material-ui/core/Typography'; 3 | import { withTheme } from '@material-ui/core/styles'; 4 | 5 | function WithTheme(props) { 6 | const { theme } = props; 7 | const primaryText = theme.palette.text.primary; 8 | const primaryColor = theme.palette.primary.main; 9 | 10 | const styles = { 11 | primaryText: { 12 | backgroundColor: theme.palette.background.default, 13 | padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`, 14 | color: primaryText, 15 | }, 16 | primaryColor: { 17 | backgroundColor: primaryColor, 18 | padding: `${theme.spacing.unit}px ${theme.spacing.unit * 2}px`, 19 | color: theme.palette.common.white, 20 | }, 21 | }; 22 | 23 | return ( 24 |
25 | {`Primary color ${primaryColor}`} 26 | {`Primary text ${primaryText}`} 27 |
28 | ); 29 | } 30 | 31 | export default withTheme()(WithTheme); 32 | -------------------------------------------------------------------------------- /server/db.js: -------------------------------------------------------------------------------- 1 | // In-memory users instead of a DB: 2 | const mockUserDB = []; 3 | 4 | /** 5 | * Retreive user from the Mock DB: 6 | * @param {User} input - {username: 'myname', password: 'mypass'} 7 | * @returns {User | undefined} 8 | */ 9 | export const retrieveUserFromMockDB = input => { 10 | const matches = mockUserDB.filter( 11 | user => 12 | user.username === input.username && 13 | user.password === input.password 14 | ); 15 | 16 | return matches[0]; 17 | }; 18 | 19 | /** 20 | * Check user exists in Mock DB: 21 | * @param {User} input 22 | * @returns {Boolean} 23 | */ 24 | export const userExistsInMockDB = input => { 25 | const matches = mockUserDB.filter(user => user.username === input.username); 26 | return matches.length > 0; 27 | }; 28 | 29 | /** 30 | * Insert user into the Mock DB: 31 | * @param {User} input 32 | * 33 | * @returns {Void} 34 | */ 35 | export const insertUserToMockDB = input => { 36 | mockUserDB.push({ 37 | name: input.body.name, 38 | uuid: input.uuid, 39 | trayId: input.trayId, 40 | username: input.body.username, 41 | password: input.body.password, 42 | }); 43 | }; 44 | -------------------------------------------------------------------------------- /src/components/View.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Nav from './Nav'; 3 | import { withTheme } from '@material-ui/core/styles'; 4 | 5 | class View extends React.Component { 6 | render() { 7 | const styles = { 8 | header: { 9 | backgroundColor: "#2196F3", 10 | padding: "12px 10px", 11 | color: "white", 12 | fontWeight: 500, 13 | fontSize: "1.3rem", 14 | }, 15 | container: { 16 | backgroundColor: "#F5F5F5", 17 | display: "flex", 18 | minHeight: 500, 19 | paddingBottom: 40, 20 | }, 21 | content: {width: "100%"}, 22 | }; 23 | 24 | return ( 25 |
26 |
OEM Demo Application
27 |
28 |
33 |
34 | ); 35 | } 36 | } 37 | 38 | export default withTheme()(View); 39 | -------------------------------------------------------------------------------- /src/lib/authWindow.js: -------------------------------------------------------------------------------- 1 | export const openAuthWindow = (url) => { 2 | // Must open window from user interaction code otherwise it is likely 3 | // to be blocked by a popup blocker: 4 | const authWindow = window.open( 5 | undefined, 6 | '_blank', 7 | 'width=500,height=500,scrollbars=no', 8 | ); 9 | 10 | const onmessage = e => { 11 | console.log('message', e.data.type, e.data); 12 | 13 | if (e.data.type === 'tray.authPopup.error') { 14 | // Handle popup error message 15 | alert(`Error: ${e.data.error}`); 16 | authWindow.close(); 17 | } 18 | if (e.data.type === 'tray.authpopup.close' || e.data.type === 'tray.authpopup.finish') { 19 | authWindow.close(); 20 | } 21 | }; 22 | window.addEventListener('message', onmessage); 23 | 24 | // Check if popup window has been closed 25 | const CHECK_TIMEOUT = 1000; 26 | const checkClosedWindow = () => { 27 | if (authWindow.closed) { 28 | window.removeEventListener('message', onmessage); 29 | } else { 30 | setTimeout(checkClosedWindow, CHECK_TIMEOUT); 31 | } 32 | } 33 | 34 | checkClosedWindow(); 35 | authWindow.location = url; 36 | }; 37 | -------------------------------------------------------------------------------- /src/Router.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 3 | import Login from "./components/auth/Login"; 4 | import Register from "./components/auth/Register"; 5 | import { PrivateRoute, RedirectMain } from "./components/auth/Auth"; 6 | 7 | import Demo from "./views/Demo"; 8 | import Account from "./views/Account"; 9 | import SolutionsMine from "./views/SolutionsMine"; 10 | import SolutionsDiscover from "./views/SolutionsDiscover"; 11 | import Authentications from "./views/Authentications"; 12 | 13 | const App = () => ( 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | ); 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /server/gqlclient.js: -------------------------------------------------------------------------------- 1 | import fetch from "node-fetch"; 2 | import { HttpLink } from 'apollo-link-http'; 3 | import { setContext } from 'apollo-link-context'; 4 | import { ApolloClient } from 'apollo-client'; 5 | import { InMemoryCache } from 'apollo-cache-inmemory'; 6 | 7 | const gqlEndpoint = process.env.TRAY_ENDPOINT; 8 | const masterToken = process.env.TRAY_MASTER_TOKEN; 9 | 10 | const defaultOptions = { 11 | watchQuery: { 12 | fetchPolicy: 'network-only', 13 | errorPolicy: 'ignore', 14 | }, 15 | query: { 16 | fetchPolicy: 'network-only', 17 | errorPolicy: 'all', 18 | }, 19 | }; 20 | 21 | // Create a Apollo Client Context for a given auth token: 22 | const authLink = token => 23 | setContext((_, {headers}) => ({ 24 | headers: { 25 | ...headers, 26 | authorization: `Bearer ${token}`, 27 | }, 28 | })); 29 | 30 | // Generate an Apollo Client for a given auth token: 31 | const generateClient = token => 32 | new ApolloClient({ 33 | link: authLink(token).concat(new HttpLink({uri: gqlEndpoint, fetch})), 34 | cache: new InMemoryCache(), 35 | defaultOptions, 36 | }); 37 | 38 | module.exports = { 39 | generateClient, 40 | masterClient: generateClient(masterToken), 41 | }; 42 | -------------------------------------------------------------------------------- /src/api/me.js: -------------------------------------------------------------------------------- 1 | export const me = () => 2 | fetch('/api/me', {credentials: 'include'}) 3 | .then(async res => ({ 4 | ok: res.ok, 5 | body: await res.json(), 6 | statusText: res.statusText, 7 | })) 8 | 9 | export const listAuths = () => 10 | fetch('/api/auths', {credentials: 'include'}) 11 | .then(async res => ({ 12 | ok: res.ok, 13 | body: await res.json(), 14 | })) 15 | 16 | export const getAuthEditUrl = (authId) => 17 | fetch('/api/auth', { 18 | body: JSON.stringify({ 19 | authId 20 | }), 21 | headers: { 22 | 'content-type': 'application/json', 23 | }, 24 | method: 'POST', 25 | credentials: 'include' 26 | }) 27 | .then(async res => ({ 28 | ok: res.ok, 29 | body: await res.json(), 30 | })) 31 | 32 | export const getAuthCreateUrl = (solutionInstanceId, externalAuthId) => 33 | fetch('/api/auth/create', { 34 | body: JSON.stringify({ 35 | solutionInstanceId, 36 | externalAuthId 37 | }), 38 | headers: { 39 | 'content-type': 'application/json', 40 | }, 41 | method: 'POST', 42 | credentials: 'include' 43 | }) 44 | .then(async res => ({ 45 | ok: res.ok, 46 | body: await res.json(), 47 | })) 48 | -------------------------------------------------------------------------------- /src/views/demo.css: -------------------------------------------------------------------------------- 1 | 2 | .header { 3 | color: rgb(21, 27, 38); 4 | font-size: 13px; 5 | font-family: "Helvetica", sans-serif; 6 | margin-bottom: 0; 7 | } 8 | 9 | button { 10 | background: transparent; 11 | border: 0; 12 | padding: 0; 13 | text-decoration: none; 14 | font-size: 13px; 15 | color: #14aaf5; 16 | } 17 | 18 | button:hover { 19 | color: #32c1ff; 20 | text-decoration: underline; 21 | cursor: pointer; 22 | } 23 | 24 | .integration-name { 25 | font-weight: 600; 26 | } 27 | 28 | .activate { 29 | float: right; 30 | color: rgb(183, 191, 198); 31 | font-size: 13px; 32 | } 33 | 34 | .activate:hover { 35 | cursor: pointer; 36 | text-decoration: underline; 37 | } 38 | 39 | p { 40 | font-size: 13px; 41 | line-height: 20px; 42 | margin: 0; 43 | } 44 | 45 | .footer { 46 | margin-top: 20px; 47 | padding-top: 10px; 48 | border-top: 1px solid #e0e6e8; 49 | } 50 | 51 | .integration-container { 52 | padding: 5px 0; 53 | } 54 | 55 | .deactivate { 56 | float: right; 57 | } 58 | 59 | .reconfigure { 60 | margin-right: 5px; 61 | float: right; 62 | } 63 | 64 | .reconfigure:hover, .deactivate:hover { 65 | cursor: pointer; 66 | } 67 | 68 | .workflow-header { 69 | color: #848f99; 70 | font-size: 11px; 71 | border-bottom: 1px solid #e0e6e8; 72 | width: 100%; 73 | margin-bottom: 5px; 74 | } 75 | 76 | .app-name { 77 | margin-bottom: 5px; 78 | } 79 | 80 | .demo-container { 81 | padding: 20px; 82 | background: white; 83 | } 84 | -------------------------------------------------------------------------------- /graphql.config.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "README_schema" : "Specifies how to load the GraphQL schema that completion, error highlighting, and documentation is based on in the IDE", 4 | "schema": { 5 | 6 | "README_file" : "Remove 'file' to use request url below. A relative or absolute path to the JSON from a schema introspection query, e.g. '{ data: ... }' or a .graphql/.graphqls file describing the schema using GraphQL Schema Language. Changes to the file are watched.", 7 | "file": "graphql.schema.json", 8 | 9 | "README_request" : "To request the schema from a url instead, remove the 'file' JSON property above (and optionally delete the default graphql.schema.json file).", 10 | "request": { 11 | "url" : "https://tray.io/graphql", 12 | "method" : "POST", 13 | "README_postIntrospectionQuery" : "Whether to POST an introspectionQuery to the url. If the url always returns the schema JSON, set to false and consider using GET", 14 | "postIntrospectionQuery" : true, 15 | "README_options" : "See the 'Options' section at https://github.com/then/then-request", 16 | "options" : { 17 | "headers": { 18 | "user-agent" : "JS GraphQL" 19 | } 20 | } 21 | } 22 | 23 | }, 24 | 25 | "README_endpoints": "A list of GraphQL endpoints that can be queried from '.graphql' files in the IDE", 26 | "endpoints" : [ 27 | { 28 | "name": "TrayOEM", 29 | "url": "https://tray.io/graphql", 30 | "options" : { 31 | "headers": { 32 | "user-agent" : "JS GraphQL" 33 | } 34 | } 35 | } 36 | ] 37 | 38 | } -------------------------------------------------------------------------------- /src/components/AuthWizard.js: -------------------------------------------------------------------------------- 1 | import { withTheme } from "@material-ui/core/styles/index"; 2 | import React from "react"; 3 | 4 | export class AuthWizard extends React.PureComponent { 5 | state = {}; 6 | 7 | constructor(props) { 8 | super(props); 9 | this.iframe = React.createRef(); 10 | } 11 | 12 | componentDidMount() { 13 | window.addEventListener("message", this.handleIframeEvents); 14 | } 15 | 16 | componentWillUnmount() { 17 | window.removeEventListener("message", this.handleIframeEvents); 18 | } 19 | 20 | handleIframeEvents = (e) => { 21 | console.log(`${e.data.type} event received`); 22 | // Here we should handle all event types 23 | if (e.data.type === "tray.authPopup.error") { 24 | alert(`Error: ${e.data.error}`); 25 | } 26 | if (e.data.type === "tray.authpopup.close") { 27 | this.props.onClose(); 28 | } 29 | if (e.data.type === "tray.authpopup.finish") { 30 | this.props.onClose(); 31 | } 32 | }; 33 | 34 | render() { 35 | const styles = { 36 | container: { 37 | flex: 1, 38 | position: "relative", 39 | }, 40 | iframe: { 41 | width: "100%", 42 | height: "100%", 43 | minHeight: "500px", 44 | border: "1px solid #2196f3", 45 | borderRadius: "4px", 46 | boxSizing: "border-box", 47 | }, 48 | }; 49 | 50 | return ( 51 |
52 |