feat: working unraid-api gql

This commit is contained in:
Zack Spear
2023-07-19 13:09:01 -07:00
parent c68d5e17f0
commit 3b4c4ed552
13 changed files with 6321 additions and 528 deletions
+54
View File
@@ -0,0 +1,54 @@
import type { CodegenConfig } from '@graphql-codegen/cli';
const getApiCodegenUrl = () => {
if (process.env.USE_LOCAL_CODEGEN === 'true') {
return 'http://localhost:3001/graphql';
}
return '';
};
const config: CodegenConfig = {
overwrite: true,
documents: ['./**/**/*.ts'],
ignoreNoDocuments: false,
config: {
namingConvention: {
typeNames: './fix-array-type.ts',
},
scalars: {
DateTime: 'string',
Long: 'number',
JSON: 'string',
URL: 'URL',
Port: 'number',
UUID: 'string',
},
},
generates: {
'composables/gql/': {
preset: 'client',
config: {
useTypeImports: true,
},
schema: [
{
'http://localhost:3001/graphql': {
headers: {
origin: `/var/run/unraid-php.sock`,
'x-api-key': 'unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810',
},
},
},
],
plugins: [
{
add: {
content: '/* eslint-disable */',
},
},
],
},
},
};
export default config;
@@ -0,0 +1,15 @@
import { graphql } from "~/composables/gql/gql";
export const TEST_FRAGMENT = graphql(/* GraphQL */`
fragment TestFragment on Cloud {
error
}
`);
export const TEST_QUERY = graphql(/* GraphQL */`
query cloudError {
cloud {
...TestFragment
}
}
`);
@@ -1,8 +1,21 @@
<script setup lang="ts">
import { ExclamationTriangleIcon, CheckCircleIcon } from '@heroicons/vue/24/solid';
// import { storeToRefs } from 'pinia';
// import { useServerStore } from '~/store/server';
// const { stateData } = storeToRefs(useServerStore());
import { useQuery } from '@vue/apollo-composable';
import {
TEST_FRAGMENT,
TEST_QUERY,
} from './DropdownConnectStatus.fragment';
import { useFragment } from '@/composables/gql/fragment-masking';
const { result: newResult } = useQuery(
TEST_QUERY,
);
const result = computed(() => useFragment(TEST_FRAGMENT, newResult.value?.cloud));
watch(result, (newVal, oldVal) => {
console.log('result', newVal, oldVal);
});
type ApiOnlineStatus = 'online'|'offline';
const onlineStatus = ref<ApiOnlineStatus>('online');
+66
View File
@@ -0,0 +1,66 @@
import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core';
import type { FragmentDefinitionNode } from 'graphql';
import type { Incremental } from './graphql';
export type FragmentType<TDocumentType extends DocumentTypeDecoration<any, any>> = TDocumentType extends DocumentTypeDecoration<
infer TType,
any
>
? [TType] extends [{ ' $fragmentName'?: infer TKey }]
? TKey extends string
? { ' $fragmentRefs'?: { [key in TKey]: TType } }
: never
: never
: never;
// return non-nullable if `fragmentType` is non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>>
): TType;
// return nullable if `fragmentType` is nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | null | undefined
): TType | null | undefined;
// return array of non-nullable if `fragmentType` is array of non-nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>>
): ReadonlyArray<TType>;
// return array of nullable if `fragmentType` is array of nullable
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): ReadonlyArray<TType> | null | undefined;
export function useFragment<TType>(
_documentNode: DocumentTypeDecoration<TType, any>,
fragmentType: FragmentType<DocumentTypeDecoration<TType, any>> | ReadonlyArray<FragmentType<DocumentTypeDecoration<TType, any>>> | null | undefined
): TType | ReadonlyArray<TType> | null | undefined {
return fragmentType as any;
}
export function makeFragmentData<
F extends DocumentTypeDecoration<any, any>,
FT extends ResultOf<F>
>(data: FT, _fragment: F): FragmentType<F> {
return data as FragmentType<F>;
}
export function isFragmentReady<TQuery, TFrag>(
queryNode: DocumentTypeDecoration<TQuery, any>,
fragmentNode: TypedDocumentNode<TFrag>,
data: FragmentType<TypedDocumentNode<Incremental<TFrag>, any>> | null | undefined
): data is FragmentType<typeof fragmentNode> {
const deferredFields = (queryNode as { __meta__?: { deferredFields: Record<string, (keyof TFrag)[]> } }).__meta__
?.deferredFields;
if (!deferredFields) return true;
const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined;
const fragName = fragDef?.name?.value;
const fields = (fragName && deferredFields[fragName]) || [];
return fields.length > 0 && fields.every(field => data && field in data);
}
+47
View File
@@ -0,0 +1,47 @@
/* eslint-disable */
import * as types from './graphql';
import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
/**
* Map of all GraphQL operations in the project.
*
* This map has several performance disadvantages:
* 1. It is not tree-shakeable, so it will include all operations in the project.
* 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle.
* 3. It does not support dead code elimination, so it will add unused operations.
*
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
"\n fragment TestFragment on Cloud {\n error\n }\n": types.TestFragmentFragmentDoc,
"\n query cloudError {\n cloud {\n ...TestFragment\n }\n }\n": types.cloudErrorDocument,
};
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*
*
* @example
* ```ts
* const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`);
* ```
*
* The query argument is unknown!
* Please regenerate the types.
*/
export function graphql(source: string): unknown;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment TestFragment on Cloud {\n error\n }\n"): (typeof documents)["\n fragment TestFragment on Cloud {\n error\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query cloudError {\n cloud {\n ...TestFragment\n }\n }\n"): (typeof documents)["\n query cloudError {\n cloud {\n ...TestFragment\n }\n }\n"];
export function graphql(source: string) {
return (documents as any)[source] ?? {};
}
export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
export * from "./fragment-masking";
export * from "./gql";
+3 -5
View File
@@ -1,5 +1,5 @@
import { DefaultApolloClient } from '@vue/apollo-composable';
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core';
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core/core.cjs';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { createClient } from 'graphql-ws';
@@ -10,7 +10,7 @@ export interface ApolloClientPayload {
wsEndpoint: string;
}
const useApollo = (payload: ApolloClientPayload) => {
export const useApollo = (payload: ApolloClientPayload) => {
console.debug('[useApollo.create]', payload);
const unraidApiClient = ref<ApolloClient<any>>();
@@ -54,7 +54,7 @@ const useApollo = (payload: ApolloClientPayload) => {
// Create the apollo client with cache implementation.
unraidApiClient.value = new ApolloClient({
// link,
link,
cache: new InMemoryCache(),
});
@@ -62,5 +62,3 @@ const useApollo = (payload: ApolloClientPayload) => {
unraidApiClient,
};
};
export default useApollo;
+17
View File
@@ -0,0 +1,17 @@
/**
* This function wraps constant case, that turns any string into CONSTANT_CASE
* However, this function has a bug that, if you pass _ to it it will return an empty
* string. This small module fixes that
*
* @param {string*} str
* @return {string}
*/
function FixArrayType(str) {
if (str === 'Array') {
return 'ArrayType';
}
// If result is an empty string, just return the original string
return str;
}
module.exports = FixArrayType;
+4488 -506
View File
File diff suppressed because it is too large Load Diff
+6 -1
View File
@@ -9,9 +9,14 @@
"postinstall": "nuxt prepare",
"serve": "serve dist/nuxt-custom-elements/connect-components",
"deploy:dev": "./scripts/deploy-dev.sh",
"build:dev": "nuxt build && npm run deploy:dev"
"build:dev": "nuxt build && npm run deploy:dev",
"codegen": "graphql-codegen --config codegen.ts -r dotenv/config",
"codegen:watch": "graphql-codegen --config codegen.ts --watch -r dotenv/config"
},
"devDependencies": {
"@graphql-codegen/cli": "^4.0.1",
"@graphql-codegen/client-preset": "^4.0.1",
"@graphql-codegen/introspection": "^4.0.0",
"@nuxt/devtools": "^0.6.1",
"@nuxtjs/tailwindcss": "^6.7.0",
"@types/crypto-js": "^4.1.1",
+1 -1
View File
@@ -661,7 +661,7 @@ export const useServerStore = defineStore('server', () => {
if (newVal) {
// start new client
console.debug('[useUnraidApiStore] apiKey start new client');
unraidApiStore.createApolloClient();
unraidApiStore.createApolloClient(newVal);
}
});
watch(theme, (newVal) => {
+6 -12
View File
@@ -1,7 +1,6 @@
import { provideApolloClient } from '@vue/apollo-composable';
import { defineStore, createPinia, setActivePinia } from 'pinia';
import useApollo from '~/composables/services/apollo';
import { useApollo } from '~/composables/services/apollo';
/**
* @see https://stackoverflow.com/questions/73476371/using-pinia-with-vue-js-web-components
@@ -9,6 +8,7 @@ import useApollo from '~/composables/services/apollo';
*/
setActivePinia(createPinia());
// @todo build URLs correctly
const WINDOW_URL = new URL(window.location.origin);
const httpEndpoint = `${WINDOW_URL.protocol}//${WINDOW_URL.host}/graphql`;
const wsProtocol = WINDOW_URL.protocol.includes('https') ? 'wss://' : 'ws://';
@@ -17,24 +17,18 @@ const wsEndpoint = `${wsProtocol}${WINDOW_URL.host}/graphql`;
export const useUnraidApiStore = defineStore('unraidApi', () => {
console.debug('[useUnraidApiStore]');
const apiKey = ref<string>('');
const setApiKey = (newApiKey: string) => apiKey.value = newApiKey;
const createApolloClient = () => {
const createApolloClient = (apiKey: string) => {
console.debug('[useUnraidApiStore] createClient');
const { unraidApiClient} = useApollo({
apiKey: apiKey.value,
const { unraidApiClient } = useApollo({
apiKey,
httpEndpoint,
wsEndpoint,
});
console.debug('[useUnraidApiStore] provideApolloClient', unraidApiClient.value);
// provideApolloClient(unraidApiClient);
provideApolloClient(unraidApiClient.value);
};
return {
apiKey,
setApiKey,
createApolloClient,
};
});