mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 17:08:41 -06:00
9856 add wrapper to graphiql
This commit is contained in:
parent
371a2a29ca
commit
f456731929
@ -1,6 +1,10 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.views import redirect_to_login
|
from django.contrib.auth.views import redirect_to_login
|
||||||
from django.http import HttpResponseNotFound, HttpResponseForbidden
|
from django.http import HttpResponseNotFound, HttpResponseForbidden
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.template import loader
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from rest_framework.exceptions import AuthenticationFailed
|
from rest_framework.exceptions import AuthenticationFailed
|
||||||
@ -42,3 +46,9 @@ class NetBoxGraphQLView(GraphQLView):
|
|||||||
return HttpResponseForbidden("No credentials provided.")
|
return HttpResponseForbidden("No credentials provided.")
|
||||||
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def render_graphql_ide(self, request):
|
||||||
|
template = loader.get_template("graphiql.html")
|
||||||
|
context = {"SUBSCRIPTION_ENABLED": json.dumps(self.subscriptions_enabled)}
|
||||||
|
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
@ -474,6 +474,10 @@ STATICFILES_DIRS = (
|
|||||||
os.path.join(BASE_DIR, 'project-static', 'img'),
|
os.path.join(BASE_DIR, 'project-static', 'img'),
|
||||||
os.path.join(BASE_DIR, 'project-static', 'js'),
|
os.path.join(BASE_DIR, 'project-static', 'js'),
|
||||||
('docs', os.path.join(BASE_DIR, 'project-static', 'docs')), # Prefix with /docs
|
('docs', os.path.join(BASE_DIR, 'project-static', 'docs')), # Prefix with /docs
|
||||||
|
# os.path.join(ROOT_DIR, 'node_modules', 'graphiql-explorer'),
|
||||||
|
# os.path.join(ROOT_DIR, 'node_modules', 'react', 'cjs'),
|
||||||
|
# os.path.join(ROOT_DIR, 'node_modules', 'react_dom', 'cjs'),
|
||||||
|
# os.path.join(ROOT_DIR, 'node_modules', 'js-cookie', 'dist'),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Media
|
# Media
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
const esbuild = require('esbuild');
|
const esbuild = require('esbuild');
|
||||||
const { sassPlugin } = require('esbuild-sass-plugin');
|
const { sassPlugin } = require('esbuild-sass-plugin');
|
||||||
|
const util = require('util');
|
||||||
|
const fs = require('fs');
|
||||||
|
const copyFilePromise = util.promisify(fs.copyFile);
|
||||||
|
|
||||||
// Bundler options common to all bundle jobs.
|
// Bundler options common to all bundle jobs.
|
||||||
const options = {
|
const options = {
|
||||||
@ -14,24 +17,49 @@ const options = {
|
|||||||
// Get CLI arguments for optional overrides.
|
// Get CLI arguments for optional overrides.
|
||||||
const ARGS = process.argv.slice(2);
|
const ARGS = process.argv.slice(2);
|
||||||
|
|
||||||
|
function copyFiles(files) {
|
||||||
|
return Promise.all(files.map(f => {
|
||||||
|
return copyFilePromise(f.source, f.dest);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async function bundleGraphIQL() {
|
async function bundleGraphIQL() {
|
||||||
try {
|
fileMap = [
|
||||||
const result = await esbuild.build({
|
{
|
||||||
...options,
|
source: './node_modules/react/umd/react.production.min.js',
|
||||||
entryPoints: {
|
dest: './dist/react.production.min.js'
|
||||||
graphiql: 'netbox-graphiql/index.ts',
|
},
|
||||||
},
|
{
|
||||||
target: 'es2016',
|
source: './node_modules/react-dom/umd/react-dom.production.min.js',
|
||||||
define: {
|
dest: './dist/react-dom.production.min.js'
|
||||||
global: 'window',
|
},
|
||||||
},
|
{
|
||||||
});
|
source: './node_modules/js-cookie/dist/js.cookie.min.js',
|
||||||
if (result.errors.length === 0) {
|
dest: './dist/js.cookie.min.js'
|
||||||
console.log(`✅ Bundled source file 'netbox-graphiql/index.ts' to 'graphiql.js'`);
|
},
|
||||||
|
{
|
||||||
|
source: './node_modules/graphiql/graphiql.min.js',
|
||||||
|
dest: './dist/graphiql.min.js'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: './node_modules/@graphiql/plugin-explorer/dist/index.umd.js',
|
||||||
|
dest: './dist/index.umd.js'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: './node_modules/graphiql/graphiql.min.css',
|
||||||
|
dest: './dist/graphiql.min.css'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: './node_modules/@graphiql/plugin-explorer/dist/style.css',
|
||||||
|
dest: './dist/plugin-explorer-style.css'
|
||||||
}
|
}
|
||||||
} catch (err) {
|
]
|
||||||
console.error(err);
|
|
||||||
}
|
copyFiles(fileMap).then(() => {
|
||||||
|
console.log('✅ Copied graphiql files');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,7 +105,6 @@ async function bundleStyles() {
|
|||||||
'netbox': 'styles/netbox.scss',
|
'netbox': 'styles/netbox.scss',
|
||||||
rack_elevation: 'styles/svg/rack_elevation.scss',
|
rack_elevation: 'styles/svg/rack_elevation.scss',
|
||||||
cable_trace: 'styles/svg/cable_trace.scss',
|
cable_trace: 'styles/svg/cable_trace.scss',
|
||||||
graphiql: 'netbox-graphiql/graphiql.scss',
|
|
||||||
};
|
};
|
||||||
const pluginOptions = { outputStyle: 'compressed' };
|
const pluginOptions = { outputStyle: 'compressed' };
|
||||||
// Allow cache disabling.
|
// Allow cache disabling.
|
||||||
|
BIN
netbox/project-static/dist/graphiql.css
vendored
BIN
netbox/project-static/dist/graphiql.css
vendored
Binary file not shown.
BIN
netbox/project-static/dist/graphiql.min.css
vendored
Normal file
BIN
netbox/project-static/dist/graphiql.min.css
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/graphiql.min.js
vendored
Normal file
BIN
netbox/project-static/dist/graphiql.min.js
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/index.umd.js
vendored
Normal file
BIN
netbox/project-static/dist/index.umd.js
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/js.cookie.min.js
vendored
Normal file
BIN
netbox/project-static/dist/js.cookie.min.js
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/plugin-explorer-style.css
vendored
Normal file
BIN
netbox/project-static/dist/plugin-explorer-style.css
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/react-dom.production.min.js
vendored
Normal file
BIN
netbox/project-static/dist/react-dom.production.min.js
vendored
Normal file
Binary file not shown.
BIN
netbox/project-static/dist/react.production.min.js
vendored
Normal file
BIN
netbox/project-static/dist/react.production.min.js
vendored
Normal file
Binary file not shown.
@ -1,3 +0,0 @@
|
|||||||
// Rather than use CDNs to include GraphiQL dependencies, import and bundle the dependencies so
|
|
||||||
// they can be locally served.
|
|
||||||
@import '../node_modules/graphiql/graphiql.css';
|
|
@ -1,17 +0,0 @@
|
|||||||
/**
|
|
||||||
* Rather than use CDNs to include GraphiQL dependencies, import and bundle the dependencies so
|
|
||||||
* they can be locally served.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import * as ReactDOM from 'react-dom';
|
|
||||||
import 'graphql';
|
|
||||||
import GraphiQL from 'graphiql';
|
|
||||||
import SubscriptionsTransportWs from 'subscriptions-transport-ws';
|
|
||||||
|
|
||||||
window.React = React;
|
|
||||||
window.ReactDOM = ReactDOM;
|
|
||||||
// @ts-expect-error Assigning to window is required for graphene-django
|
|
||||||
window.SubscriptionsTransportWs = SubscriptionsTransportWs;
|
|
||||||
// @ts-expect-error Assigning to window is required for graphene-django
|
|
||||||
window.GraphiQL = GraphiQL;
|
|
@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "netbox-graphiql",
|
"name": "netbox-graphiql",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"description": "NetBox GraphiQL Custom Front End",
|
"description": "NetBox GraphiQL Custom Front End",
|
||||||
"main": "dist/graphiql.js",
|
"main": "dist/graphiql.js",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"graphiql": "1.8.9",
|
"graphiql": "3.0.9",
|
||||||
"graphql": ">= v14.5.0 <= 15.5.0",
|
"graphql": "16.8.1",
|
||||||
"react": "17.0.2",
|
"react": "18.2.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "18.2.0",
|
||||||
"subscriptions-transport-ws": "0.9.18",
|
"react-scripts": "5.0.1",
|
||||||
"whatwg-fetch": "3.6.2"
|
"js-cookie": "3.0.5",
|
||||||
|
"@graphiql/plugin-explorer": "1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{% comment %}
|
{% comment %}
|
||||||
This template derives from the graphene-django project:
|
This template derives from the strawberry-graphql project:
|
||||||
https://github.com/graphql-python/graphene-django/blob/main/graphene_django/templates/graphene/graphiql.html
|
https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/static/graphiql.html
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
<!--
|
<!--
|
||||||
The request to this GraphQL server provided the header "Accept: text/html"
|
The request to this GraphQL server provided the header "Accept: text/html"
|
||||||
@ -11,36 +11,130 @@ add "&raw" to the end of the URL within a browser.
|
|||||||
-->
|
-->
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<title>GraphiQL | NetBox</title>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
href="data:image/svg+xml,
|
||||||
|
<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22>
|
||||||
|
<!-- Strawberry Emoji as a HTML Entity (hex) -->
|
||||||
|
<text y=%22.9em%22 font-size=%2280%22>🍓</text>
|
||||||
|
</svg>"
|
||||||
|
/>
|
||||||
<style>
|
<style>
|
||||||
html, body, #editor {
|
body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#graphiql {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docExplorerHide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-explorer-contents {
|
||||||
|
overflow-y: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docExplorerWrap {
|
||||||
|
width: unset !important;
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graphiql-explorer-actions select {
|
||||||
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link href="{% static 'graphiql.css'%}" rel="stylesheet" />
|
|
||||||
<link rel="icon" type="image/png" href="{% static 'graphql.ico' %}" />
|
<script src="{% static 'react.production.min.js' %}"></script>
|
||||||
<title>GraphiQL | NetBox</title>
|
<script src="{% static 'react-dom.production.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js.cookie.min.js' %}"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{% static 'graphiql.min.css' %}"/>
|
||||||
|
<link rel="stylesheet" href="{% static 'plugin-explorer-style.css' %}"/>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="editor"></div>
|
<div id="graphiql" class="graphiql-container">Loading...</div>
|
||||||
{% csrf_token %}
|
<script src="{% static 'graphiql.min.js' %}"></script>
|
||||||
<script type="application/javascript">
|
<script src="{% static 'index.umd.js' %}"></script>
|
||||||
window.GRAPHENE_SETTINGS = {
|
|
||||||
{% if subscription_path %}
|
<script>
|
||||||
subscriptionPath: "{{subscription_path}}",
|
const EXAMPLE_QUERY = `# Welcome to GraphiQL 🍓
|
||||||
{% endif %}
|
#
|
||||||
graphiqlHeaderEditorEnabled: {{ graphiql_header_editor_enabled|yesno:"true,false" }},
|
# GraphiQL is an in-browser tool for writing, validating, and
|
||||||
};
|
# testing GraphQL queries.
|
||||||
|
#
|
||||||
|
# Type queries into this side of the screen, and you will see intelligent
|
||||||
|
# typeaheads aware of the current GraphQL type schema and live syntax and
|
||||||
|
# validation errors highlighted within the text.
|
||||||
|
#
|
||||||
|
# GraphQL queries typically start with a "{" character. Lines that starts
|
||||||
|
# with a # are ignored.
|
||||||
|
#
|
||||||
|
# An example GraphQL query might look like:
|
||||||
|
#
|
||||||
|
# {
|
||||||
|
# field(arg: "value") {
|
||||||
|
# subField
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Keyboard shortcuts:
|
||||||
|
#
|
||||||
|
# Run Query: Ctrl-Enter (or press the play button above)
|
||||||
|
#
|
||||||
|
# Auto Complete: Ctrl-Space (or just start typing)
|
||||||
|
#
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fetchURL = window.location.href;
|
||||||
|
|
||||||
|
function httpUrlToWebSockeUrl(url) {
|
||||||
|
const parsedURL = new URL(url);
|
||||||
|
const protocol = parsedURL.protocol === "http:" ? "ws:" : "wss:";
|
||||||
|
parsedURL.protocol = protocol;
|
||||||
|
parsedURL.hash = "";
|
||||||
|
return parsedURL.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = {};
|
||||||
|
const csrfToken = Cookies.get("csrftoken");
|
||||||
|
|
||||||
|
if (csrfToken) {
|
||||||
|
headers["x-csrftoken"] = csrfToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptionsEnabled = JSON.parse("{{ SUBSCRIPTION_ENABLED }}");
|
||||||
|
const subscriptionUrl = subscriptionsEnabled
|
||||||
|
? httpUrlToWebSockeUrl(fetchURL)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const fetcher = GraphiQL.createFetcher({
|
||||||
|
url: fetchURL,
|
||||||
|
headers: headers,
|
||||||
|
subscriptionUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
const explorerPlugin = GraphiQLPluginExplorer.explorerPlugin();
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById("graphiql"));
|
||||||
|
|
||||||
|
root.render(
|
||||||
|
React.createElement(GraphiQL, {
|
||||||
|
fetcher: fetcher,
|
||||||
|
defaultEditorToolsVisibility: true,
|
||||||
|
plugins: [explorerPlugin],
|
||||||
|
inputValueDeprecation: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
<script
|
|
||||||
type="text/javascript"
|
|
||||||
src="{% static 'graphiql.js' %}"
|
|
||||||
onerror="window.location='{% url 'media_failure' %}?filename=graphiql.js'">
|
|
||||||
</script>
|
|
||||||
<script src="{% static 'graphene_django/graphiql.js' %}"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue
Block a user