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.contrib.auth.views import redirect_to_login
from django.http import HttpResponseNotFound, HttpResponseForbidden
from django.http import HttpResponse
from django.template import loader
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.exceptions import AuthenticationFailed
@ -42,3 +46,9 @@ class NetBoxGraphQLView(GraphQLView):
return HttpResponseForbidden("No credentials provided.")
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', 'js'),
('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

View File

@ -1,5 +1,8 @@
const esbuild = require('esbuild');
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.
const options = {
@ -14,24 +17,49 @@ const options = {
// Get CLI arguments for optional overrides.
const ARGS = process.argv.slice(2);
function copyFiles(files) {
return Promise.all(files.map(f => {
return copyFilePromise(f.source, f.dest);
}));
}
async function bundleGraphIQL() {
try {
const result = await esbuild.build({
...options,
entryPoints: {
graphiql: 'netbox-graphiql/index.ts',
},
target: 'es2016',
define: {
global: 'window',
},
});
if (result.errors.length === 0) {
console.log(`✅ Bundled source file 'netbox-graphiql/index.ts' to 'graphiql.js'`);
fileMap = [
{
source: './node_modules/react/umd/react.production.min.js',
dest: './dist/react.production.min.js'
},
{
source: './node_modules/react-dom/umd/react-dom.production.min.js',
dest: './dist/react-dom.production.min.js'
},
{
source: './node_modules/js-cookie/dist/js.cookie.min.js',
dest: './dist/js.cookie.min.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',
rack_elevation: 'styles/svg/rack_elevation.scss',
cable_trace: 'styles/svg/cable_trace.scss',
graphiql: 'netbox-graphiql/graphiql.scss',
};
const pluginOptions = { outputStyle: 'compressed' };
// 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",
"version": "0.1.0",
"version": "0.2.0",
"description": "NetBox GraphiQL Custom Front End",
"main": "dist/graphiql.js",
"license": "Apache-2.0",
"private": true,
"dependencies": {
"graphiql": "1.8.9",
"graphql": ">= v14.5.0 <= 15.5.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"subscriptions-transport-ws": "0.9.18",
"whatwg-fetch": "3.6.2"
"graphiql": "3.0.9",
"graphql": "16.8.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-scripts": "5.0.1",
"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 %}
This template derives from the graphene-django project:
https://github.com/graphql-python/graphene-django/blob/main/graphene_django/templates/graphene/graphiql.html
This template derives from the strawberry-graphql project:
https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/static/graphiql.html
{% endcomment %}
<!--
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 %}
<!DOCTYPE html>
<html lang="en">
<html>
<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>
html, body, #editor {
body {
height: 100%;
margin: 0;
overflow: hidden;
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>
<link href="{% static 'graphiql.css'%}" rel="stylesheet" />
<link rel="icon" type="image/png" href="{% static 'graphql.ico' %}" />
<title>GraphiQL | NetBox</title>
<script src="{% static 'react.production.min.js' %}"></script>
<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>
<body>
<div id="editor"></div>
{% csrf_token %}
<script type="application/javascript">
window.GRAPHENE_SETTINGS = {
{% if subscription_path %}
subscriptionPath: "{{subscription_path}}",
{% endif %}
graphiqlHeaderEditorEnabled: {{ graphiql_header_editor_enabled|yesno:"true,false" }},
};
<div id="graphiql" class="graphiql-container">Loading...</div>
<script src="{% static 'graphiql.min.js' %}"></script>
<script src="{% static 'index.umd.js' %}"></script>
<script>
const EXAMPLE_QUERY = `# Welcome to GraphiQL 🍓
#
# 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
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>
</html>