9856 add wrapper to graphiql

This commit is contained in:
Arthur 2024-03-20 13:54:26 -07:00
parent 371a2a29ca
commit f456731929
16 changed files with 9586 additions and 208 deletions

View File

@ -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))

View File

@ -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

View File

@ -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.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
netbox/project-static/dist/index.umd.js vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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';

View File

@ -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;

View File

@ -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

View File

@ -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>&#x1f353;</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>