Skip to content

Commit

Permalink
Add GraphQL operations page
Browse files Browse the repository at this point in the history
  • Loading branch information
GavinRay97 committed Nov 17, 2020
1 parent 59a7bb1 commit c6ba5e1
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 51 deletions.
10 changes: 7 additions & 3 deletions react-app/components/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const Header = () => {
loadMetadataAndDatabaseInfo()
}}
/>
<span className="ml-3 pb-1">{role}</span>
<span className="pb-1 ml-3">{role}</span>
</label>
</div>
))}
Expand All @@ -76,9 +76,13 @@ const NavMenu = () => {
// TODO: Extract this maybe to make it cleaner
const navItems = [
{
text: "Data Models",
text: "Data Models (DB)",
route: "/",
},
{
text: "Data Models (GQL)",
route: "/graphql-operations",
},
{
text: "Data Graph",
route: "/datagraph",
Expand All @@ -93,7 +97,7 @@ const NavMenu = () => {
"flex w-full h-12 mt-6 text-center place-items-center cursor-pointer bg-white shadow-md justify-center rounded hover:shadow-lg text-gray-800 border-l-4 border-transparent pr-4 nav-tab"

return (
<nav className="w-1/4 p-6 bg-gray-300 h-screen sticky top-0">
<nav className="sticky top-0 w-1/4 h-screen p-6 bg-gray-300">
{navItems.map(({ route, text }) => (
<div
key={route}
Expand Down
134 changes: 134 additions & 0 deletions react-app/pages/graphql-operations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { useState } from "react"

import { useRouter } from "next/router"

import { useStoreState, useStoreActions } from "../store"

import { Table, TableProps } from "../components/table"
import { SearchInput } from "../components/SearchInput"

export default function DatabaseModelTableView() {
const router = useRouter()
const [search, setSearch] = useState("")
const graphqlSchema = useStoreState(store => store.graphqlSchema)
const setCurrentTryItOutOperationName = useStoreActions(
store => store.setCurrentTryItOutOperationName
)

const tryItOutButton = (
<span className="cursor-pointer try-it-out-button" role="button">
Try it out <span className="pl-2 try-it-out-icon"></span>
</span>
)

const queryType = graphqlSchema?.getQueryType()
const queryFields = queryType?.getFields()

const mutationType = graphqlSchema?.getMutationType()
const mutationFields = mutationType?.getFields()

const subscriptionType = graphqlSchema?.getSubscriptionType()
const subscriptionFields = subscriptionType?.getFields()

const graphqlOperationsColumns: TableProps["columns"] = []

for (const name in queryFields) {
const operation = queryFields[name]
if (!operation.name.toLowerCase().includes(search.toLowerCase())) continue
graphqlOperationsColumns.push({
operationType: "Query",
operationName: operation.name,
description: operation.description,
tryItOut: tryItOutButton,
})
}

for (const name in mutationFields) {
const operation = mutationFields[name]
if (!operation.name.toLowerCase().includes(search.toLowerCase())) continue
graphqlOperationsColumns.push({
operationType: "Mutation",
operationName: operation.name,
description: operation.description,
tryItOut: tryItOutButton,
})
}

for (const name in subscriptionFields) {
const operation = subscriptionFields[name]
if (!operation.name.toLowerCase().includes(search.toLowerCase())) continue
graphqlOperationsColumns.push({
operationType: "Subscription",
operationName: operation.name,
description: operation.description,
tryItOut: tryItOutButton,
})
}

return (
<div className="w-5/6 py-10 mx-auto">
<h1 className="text-3xl">GraphQL Operations</h1>
<SearchInput
className="bg-red-700"
placeholder="Search for operations"
onChange={e => setSearch(e.target.value)}
/>
<Table
headers={[
{
key: "operationType",
displayName: "Type",
},
{
key: "operationName",
displayName: "Operation Name",
onClick: column => {
router.push("/models/graphql/" + column.operationName)
},
},
{
key: "description",
displayName: "description",
},
{
key: "tryItOut",
displayName: "",
onClick: column => {
console.log(
"Try it out onClick fired, attempting to route to GraphiQL"
)
setCurrentTryItOutOperationName(column.operationName)
router.push("/graphiql")
},
},
]}
columns={graphqlOperationsColumns}
Header={header => (
<th className="px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase bg-gray-50">
{header.displayName}
</th>
)}
Cell={cell => (
<td
className="px-2 py-4 whitespace-no-wrap"
onClick={() => {
if (
cell.header.onClick &&
typeof cell.header.onClick == "function"
)
cell.header.onClick(cell.column)
}}
>
<div className="flex items-center">
<div className="ml-4">
<div className="text-sm font-medium leading-5 text-gray-900">
{cell.column[cell.header.key]}
</div>
</div>
</div>
</td>
)}
/>
</div>
)
}
49 changes: 1 addition & 48 deletions react-app/pages/models/graphql/[operation].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,7 @@ import React from "react"
import { useRouter } from "next/router"
import { useStoreState } from "../../../store"
import { Table } from "../../../components/table"
import { getNamedType, GraphQLSchema, isObjectType } from "graphql"

interface GetGraphQLOperationFieldsParams {
graphqlSchema: GraphQLSchema
operationName: string
}

/**
* Returns the GraphQLFieldMap (fields) for an operation (query/mutation/subscription)
* given a GraphQL schema and an operation name
*/
function getGraphQLOperationFields(params: GetGraphQLOperationFieldsParams) {
const queryRoot = params.graphqlSchema.getQueryType()
const mutationRoot = params.graphqlSchema.getMutationType()
const subscriptionRoot = params.graphqlSchema.getSubscriptionType()

const queries = queryRoot?.getFields()
if (queries) {
const query = queries[params.operationName]

if (query) {
const queryType = getNamedType(query.type)
if (isObjectType(queryType)) return queryType.getFields()
}
}

const mutations = mutationRoot?.getFields()
if (mutations) {
const mutation = mutations[params.operationName]

if (mutation) {
const mutationType = getNamedType(mutation.type)
if (isObjectType(mutationType)) return mutationType.getFields()
}
}

const subscriptions = subscriptionRoot?.getFields()
if (subscriptions) {
const subscription = subscriptions[params.operationName]

if (subscription) {
const subscriptionType = getNamedType(subscription.type)
if (isObjectType(subscriptionType)) return subscriptionType.getFields()
}
}

return null
}
import { getGraphQLOperationFields } from "../../../utils/misc"

export default function GraphQLOperationView() {
const router = useRouter()
Expand Down
50 changes: 50 additions & 0 deletions react-app/utils/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { GraphQLSchema, getNamedType, isObjectType } from "graphql"

interface GetGraphQLOperationFieldsParams {
graphqlSchema: GraphQLSchema
operationName: string
}

/**
* Returns the GraphQLFieldMap (fields) for an operation (query/mutation/subscription)
* given a GraphQL schema and an operation name
*/
export function getGraphQLOperationFields(
params: GetGraphQLOperationFieldsParams
) {
const queryRoot = params.graphqlSchema.getQueryType()
const mutationRoot = params.graphqlSchema.getMutationType()
const subscriptionRoot = params.graphqlSchema.getSubscriptionType()

const queries = queryRoot?.getFields()
if (queries) {
const query = queries[params.operationName]

if (query) {
const queryType = getNamedType(query.type)
if (isObjectType(queryType)) return queryType.getFields()
}
}

const mutations = mutationRoot?.getFields()
if (mutations) {
const mutation = mutations[params.operationName]

if (mutation) {
const mutationType = getNamedType(mutation.type)
if (isObjectType(mutationType)) return mutationType.getFields()
}
}

const subscriptions = subscriptionRoot?.getFields()
if (subscriptions) {
const subscription = subscriptions[params.operationName]

if (subscription) {
const subscriptionType = getNamedType(subscription.type)
if (isObjectType(subscriptionType)) return subscriptionType.getFields()
}
}

return null
}

0 comments on commit c6ba5e1

Please sign in to comment.