diff --git a/.prettierignore b/.prettierignore index 4a6de91e..b2c52005 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,3 +4,4 @@ typed-router.d.ts docs/.vitepress/cache docs/.vitepress/dist dist +assets/types/graphql.ts diff --git a/assets/graphql/releases.graphql b/assets/graphql/releases.graphql new file mode 100644 index 00000000..d7748a65 --- /dev/null +++ b/assets/graphql/releases.graphql @@ -0,0 +1,14 @@ +query GetReleases { + releases { + name + mentionsCount + tag + body + createdAt + htmlUrl + latest + features + bugFixes + breaking + } +} diff --git a/assets/modules/urql.ts b/assets/modules/urql.ts index 5571672e..499683ff 100644 --- a/assets/modules/urql.ts +++ b/assets/modules/urql.ts @@ -1,10 +1,12 @@ import { type App } from "vue"; -import urql, { cacheExchange, fetchExchange } from "@urql/vue"; +import urql, { cacheExchange, fetchExchange, Client } from "@urql/vue"; import { withBase } from "@/stores/config"; +export const client = new Client({ + url: withBase("/api/graphql"), + exchanges: [cacheExchange, fetchExchange], +}); + export const install = (app: App) => { - app.use(urql, { - url: withBase("/api/graphql"), - exchanges: [cacheExchange, fetchExchange], - }); + app.use(urql, client); }; diff --git a/assets/stores/announcements.ts b/assets/stores/announcements.ts index 481d1962..1eecbdc1 100644 --- a/assets/stores/announcements.ts +++ b/assets/stores/announcements.ts @@ -1,15 +1,9 @@ -type Announcement = { - name: string; +import { client } from "@/modules/urql"; +import { GetReleasesDocument, type Release } from "@/types/graphql"; + +type Announcement = Omit & { announcement: boolean; createdAt: Date; - body: string; - tag: string; - htmlUrl: string; - latest: boolean; - mentionsCount: number; - features: number; - bugFixes: number; - breaking: number; }; const releases = ref([]); @@ -20,8 +14,13 @@ async function fetchReleases() { fetched = true; try { - const { data } = await useFetch(withBase("/api/releases")).get().json(); - releases.value = data.value || []; + const { data } = await client.query(GetReleasesDocument, {}); + releases.value = + data?.releases?.map((r) => ({ + ...r, + createdAt: new Date(r.createdAt), + announcement: false, + })) || []; } catch (error) { console.error("Error while fetching releases:\n", error); fetched = false; @@ -35,10 +34,7 @@ if (config.releaseCheckMode === "automatic") { const otherAnnouncements = [] as Announcement[]; const announcements = computed(() => { - const newReleases = - releases.value?.map((release) => ({ ...release, createdAt: new Date(release.createdAt), announcement: false })) ?? - []; - return [...newReleases, ...otherAnnouncements].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); + return [...releases.value, ...otherAnnouncements].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); }); const mostRecent = computed(() => announcements.value?.[0]); diff --git a/assets/types/graphql.ts b/assets/types/graphql.ts index 04aaccb2..fbbb49f1 100644 --- a/assets/types/graphql.ts +++ b/assets/types/graphql.ts @@ -1,802 +1,278 @@ -import { TypedDocumentNode as DocumentNode } from "@graphql-typed-document-node/core"; +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; export type MakeEmpty = { [_ in K]?: never }; -export type Incremental = T | { [P in keyof T]?: P extends " $fragmentName" | "__typename" ? T[P] : never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: { input: string; output: string }; - String: { input: string; output: string }; - Boolean: { input: boolean; output: boolean }; - Int: { input: number; output: number }; - Float: { input: number; output: number }; - Any: { input: unknown; output: unknown }; - Int64: { input: number; output: number }; - Map: { input: Record; output: Record }; - Time: { input: string; output: string }; + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + Any: { input: unknown; output: unknown; } + Int64: { input: number; output: number; } + Map: { input: Record; output: Record; } + Time: { input: string; output: string; } }; export type Container = { - __typename?: "Container"; - command: Scalars["String"]["output"]; - created: Scalars["Time"]["output"]; - group?: Maybe; - health?: Maybe; - host?: Maybe; - id: Scalars["String"]["output"]; - image: Scalars["String"]["output"]; - labels?: Maybe; - name: Scalars["String"]["output"]; - startedAt: Scalars["Time"]["output"]; - state: Scalars["String"]["output"]; + __typename?: 'Container'; + command: Scalars['String']['output']; + created: Scalars['Time']['output']; + group?: Maybe; + health?: Maybe; + host?: Maybe; + id: Scalars['String']['output']; + image: Scalars['String']['output']; + labels?: Maybe; + name: Scalars['String']['output']; + startedAt: Scalars['Time']['output']; + state: Scalars['String']['output']; }; export type Dispatcher = { - __typename?: "Dispatcher"; - id: Scalars["Int"]["output"]; - name: Scalars["String"]["output"]; - type: Scalars["String"]["output"]; - url?: Maybe; + __typename?: 'Dispatcher'; + id: Scalars['Int']['output']; + name: Scalars['String']['output']; + type: Scalars['String']['output']; + url?: Maybe; }; export type DispatcherInput = { - name: Scalars["String"]["input"]; - type: Scalars["String"]["input"]; - url?: InputMaybe; + name: Scalars['String']['input']; + type: Scalars['String']['input']; + url?: InputMaybe; }; export type LogEvent = { - __typename?: "LogEvent"; - containerId?: Maybe; - id: Scalars["Int"]["output"]; - level?: Maybe; - message?: Maybe; - stream?: Maybe; - timestamp: Scalars["Int64"]["output"]; - type?: Maybe; + __typename?: 'LogEvent'; + containerId?: Maybe; + id: Scalars['Int']['output']; + level?: Maybe; + message?: Maybe; + stream?: Maybe; + timestamp: Scalars['Int64']['output']; + type?: Maybe; }; export type Mutation = { - __typename?: "Mutation"; + __typename?: 'Mutation'; createDispatcher: Dispatcher; createNotificationRule: NotificationRule; - deleteDispatcher: Scalars["Boolean"]["output"]; - deleteNotificationRule: Scalars["Boolean"]["output"]; + deleteDispatcher: Scalars['Boolean']['output']; + deleteNotificationRule: Scalars['Boolean']['output']; previewExpression: PreviewResult; replaceNotificationRule: NotificationRule; updateDispatcher: Dispatcher; updateNotificationRule: NotificationRule; }; + export type MutationCreateDispatcherArgs = { input: DispatcherInput; }; + export type MutationCreateNotificationRuleArgs = { input: NotificationRuleInput; }; + export type MutationDeleteDispatcherArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }; + export type MutationDeleteNotificationRuleArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }; + export type MutationPreviewExpressionArgs = { input: PreviewInput; }; + export type MutationReplaceNotificationRuleArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: NotificationRuleInput; }; + export type MutationUpdateDispatcherArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: DispatcherInput; }; + export type MutationUpdateNotificationRuleArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: NotificationRuleUpdateInput; }; export type NotificationRule = { - __typename?: "NotificationRule"; - containerExpression: Scalars["String"]["output"]; + __typename?: 'NotificationRule'; + containerExpression: Scalars['String']['output']; dispatcher?: Maybe; - enabled: Scalars["Boolean"]["output"]; - id: Scalars["Int"]["output"]; - lastTriggeredAt?: Maybe; - logExpression: Scalars["String"]["output"]; - name: Scalars["String"]["output"]; - triggerCount: Scalars["Int64"]["output"]; - triggeredContainers: Scalars["Int"]["output"]; + enabled: Scalars['Boolean']['output']; + id: Scalars['Int']['output']; + lastTriggeredAt?: Maybe; + logExpression: Scalars['String']['output']; + name: Scalars['String']['output']; + triggerCount: Scalars['Int64']['output']; + triggeredContainers: Scalars['Int']['output']; }; export type NotificationRuleInput = { - containerExpression: Scalars["String"]["input"]; - dispatcherId: Scalars["Int"]["input"]; - enabled: Scalars["Boolean"]["input"]; - logExpression: Scalars["String"]["input"]; - name: Scalars["String"]["input"]; + containerExpression: Scalars['String']['input']; + dispatcherId: Scalars['Int']['input']; + enabled: Scalars['Boolean']['input']; + logExpression: Scalars['String']['input']; + name: Scalars['String']['input']; }; export type NotificationRuleUpdateInput = { - containerExpression?: InputMaybe; - dispatcherId?: InputMaybe; - enabled?: InputMaybe; - logExpression?: InputMaybe; - name?: InputMaybe; + containerExpression?: InputMaybe; + dispatcherId?: InputMaybe; + enabled?: InputMaybe; + logExpression?: InputMaybe; + name?: InputMaybe; }; export type PreviewInput = { - containerExpression: Scalars["String"]["input"]; - logExpression?: InputMaybe; + containerExpression: Scalars['String']['input']; + logExpression?: InputMaybe; }; export type PreviewResult = { - __typename?: "PreviewResult"; - containerError?: Maybe; - logError?: Maybe; + __typename?: 'PreviewResult'; + containerError?: Maybe; + logError?: Maybe; matchedContainers: Array; matchedLogs: Array; - totalLogs: Scalars["Int"]["output"]; + totalLogs: Scalars['Int']['output']; }; export type Query = { - __typename?: "Query"; + __typename?: 'Query'; dispatcher?: Maybe; dispatchers: Array; notificationRule?: Maybe; notificationRules: Array; + releases: Array; }; + export type QueryDispatcherArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }; + export type QueryNotificationRuleArgs = { - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }; -export type GetNotificationRulesQueryVariables = Exact<{ [key: string]: never }>; - -export type GetNotificationRulesQuery = { - __typename?: "Query"; - notificationRules: Array<{ - __typename?: "NotificationRule"; - id: number; - name: string; - enabled: boolean; - containerExpression: string; - logExpression: string; - triggerCount: number; - triggeredContainers: number; - lastTriggeredAt?: string | null; - dispatcher?: { __typename?: "Dispatcher"; id: number; name: string; type: string; url?: string | null } | null; - }>; +export type Release = { + __typename?: 'Release'; + body: Scalars['String']['output']; + breaking: Scalars['Int']['output']; + bugFixes: Scalars['Int']['output']; + createdAt: Scalars['Time']['output']; + features: Scalars['Int']['output']; + htmlUrl: Scalars['String']['output']; + latest: Scalars['Boolean']['output']; + mentionsCount: Scalars['Int']['output']; + name: Scalars['String']['output']; + tag: Scalars['String']['output']; }; -export type GetDispatchersQueryVariables = Exact<{ [key: string]: never }>; +export type GetNotificationRulesQueryVariables = Exact<{ [key: string]: never; }>; -export type GetDispatchersQuery = { - __typename?: "Query"; - dispatchers: Array<{ __typename?: "Dispatcher"; id: number; name: string; type: string; url?: string | null }>; -}; + +export type GetNotificationRulesQuery = { __typename?: 'Query', notificationRules: Array<{ __typename?: 'NotificationRule', id: number, name: string, enabled: boolean, containerExpression: string, logExpression: string, triggerCount: number, triggeredContainers: number, lastTriggeredAt?: string | null, dispatcher?: { __typename?: 'Dispatcher', id: number, name: string, type: string, url?: string | null } | null }> }; + +export type GetDispatchersQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetDispatchersQuery = { __typename?: 'Query', dispatchers: Array<{ __typename?: 'Dispatcher', id: number, name: string, type: string, url?: string | null }> }; export type CreateNotificationRuleMutationVariables = Exact<{ input: NotificationRuleInput; }>; -export type CreateNotificationRuleMutation = { - __typename?: "Mutation"; - createNotificationRule: { - __typename?: "NotificationRule"; - id: number; - name: string; - enabled: boolean; - containerExpression: string; - logExpression: string; - dispatcher?: { __typename?: "Dispatcher"; id: number; name: string; type: string } | null; - }; -}; + +export type CreateNotificationRuleMutation = { __typename?: 'Mutation', createNotificationRule: { __typename?: 'NotificationRule', id: number, name: string, enabled: boolean, containerExpression: string, logExpression: string, dispatcher?: { __typename?: 'Dispatcher', id: number, name: string, type: string } | null } }; export type UpdateNotificationRuleMutationVariables = Exact<{ - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: NotificationRuleUpdateInput; }>; -export type UpdateNotificationRuleMutation = { - __typename?: "Mutation"; - updateNotificationRule: { - __typename?: "NotificationRule"; - id: number; - name: string; - enabled: boolean; - containerExpression: string; - logExpression: string; - dispatcher?: { __typename?: "Dispatcher"; id: number; name: string; type: string } | null; - }; -}; + +export type UpdateNotificationRuleMutation = { __typename?: 'Mutation', updateNotificationRule: { __typename?: 'NotificationRule', id: number, name: string, enabled: boolean, containerExpression: string, logExpression: string, dispatcher?: { __typename?: 'Dispatcher', id: number, name: string, type: string } | null } }; export type ReplaceNotificationRuleMutationVariables = Exact<{ - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: NotificationRuleInput; }>; -export type ReplaceNotificationRuleMutation = { - __typename?: "Mutation"; - replaceNotificationRule: { - __typename?: "NotificationRule"; - id: number; - name: string; - enabled: boolean; - containerExpression: string; - logExpression: string; - dispatcher?: { __typename?: "Dispatcher"; id: number; name: string; type: string } | null; - }; -}; + +export type ReplaceNotificationRuleMutation = { __typename?: 'Mutation', replaceNotificationRule: { __typename?: 'NotificationRule', id: number, name: string, enabled: boolean, containerExpression: string, logExpression: string, dispatcher?: { __typename?: 'Dispatcher', id: number, name: string, type: string } | null } }; export type DeleteNotificationRuleMutationVariables = Exact<{ - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }>; -export type DeleteNotificationRuleMutation = { __typename?: "Mutation"; deleteNotificationRule: boolean }; + +export type DeleteNotificationRuleMutation = { __typename?: 'Mutation', deleteNotificationRule: boolean }; export type CreateDispatcherMutationVariables = Exact<{ input: DispatcherInput; }>; -export type CreateDispatcherMutation = { - __typename?: "Mutation"; - createDispatcher: { __typename?: "Dispatcher"; id: number; name: string; type: string; url?: string | null }; -}; + +export type CreateDispatcherMutation = { __typename?: 'Mutation', createDispatcher: { __typename?: 'Dispatcher', id: number, name: string, type: string, url?: string | null } }; export type UpdateDispatcherMutationVariables = Exact<{ - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; input: DispatcherInput; }>; -export type UpdateDispatcherMutation = { - __typename?: "Mutation"; - updateDispatcher: { __typename?: "Dispatcher"; id: number; name: string; type: string; url?: string | null }; -}; + +export type UpdateDispatcherMutation = { __typename?: 'Mutation', updateDispatcher: { __typename?: 'Dispatcher', id: number, name: string, type: string, url?: string | null } }; export type DeleteDispatcherMutationVariables = Exact<{ - id: Scalars["Int"]["input"]; + id: Scalars['Int']['input']; }>; -export type DeleteDispatcherMutation = { __typename?: "Mutation"; deleteDispatcher: boolean }; + +export type DeleteDispatcherMutation = { __typename?: 'Mutation', deleteDispatcher: boolean }; export type PreviewExpressionMutationVariables = Exact<{ input: PreviewInput; }>; -export type PreviewExpressionMutation = { - __typename?: "Mutation"; - previewExpression: { - __typename?: "PreviewResult"; - containerError?: string | null; - logError?: string | null; - totalLogs: number; - matchedContainers: Array<{ - __typename?: "Container"; - id: string; - name: string; - image: string; - host?: string | null; - }>; - matchedLogs: Array<{ - __typename?: "LogEvent"; - id: number; - type?: string | null; - message?: unknown | null; - timestamp: number; - level?: string | null; - stream?: string | null; - }>; - }; -}; -export const GetNotificationRulesDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "query", - name: { kind: "Name", value: "GetNotificationRules" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "notificationRules" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "enabled" } }, - { kind: "Field", name: { kind: "Name", value: "containerExpression" } }, - { kind: "Field", name: { kind: "Name", value: "logExpression" } }, - { kind: "Field", name: { kind: "Name", value: "triggerCount" } }, - { kind: "Field", name: { kind: "Name", value: "triggeredContainers" } }, - { kind: "Field", name: { kind: "Name", value: "lastTriggeredAt" } }, - { - kind: "Field", - name: { kind: "Name", value: "dispatcher" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const GetDispatchersDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "query", - name: { kind: "Name", value: "GetDispatchers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "dispatchers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const CreateNotificationRuleDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "CreateNotificationRule" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { - kind: "NonNullType", - type: { kind: "NamedType", name: { kind: "Name", value: "NotificationRuleInput" } }, - }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "createNotificationRule" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "enabled" } }, - { kind: "Field", name: { kind: "Name", value: "containerExpression" } }, - { kind: "Field", name: { kind: "Name", value: "logExpression" } }, - { - kind: "Field", - name: { kind: "Name", value: "dispatcher" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const UpdateNotificationRuleDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "UpdateNotificationRule" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "Int" } } }, - }, - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { - kind: "NonNullType", - type: { kind: "NamedType", name: { kind: "Name", value: "NotificationRuleUpdateInput" } }, - }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "updateNotificationRule" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "id" }, - value: { kind: "Variable", name: { kind: "Name", value: "id" } }, - }, - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "enabled" } }, - { kind: "Field", name: { kind: "Name", value: "containerExpression" } }, - { kind: "Field", name: { kind: "Name", value: "logExpression" } }, - { - kind: "Field", - name: { kind: "Name", value: "dispatcher" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const ReplaceNotificationRuleDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "ReplaceNotificationRule" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "Int" } } }, - }, - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { - kind: "NonNullType", - type: { kind: "NamedType", name: { kind: "Name", value: "NotificationRuleInput" } }, - }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "replaceNotificationRule" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "id" }, - value: { kind: "Variable", name: { kind: "Name", value: "id" } }, - }, - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "enabled" } }, - { kind: "Field", name: { kind: "Name", value: "containerExpression" } }, - { kind: "Field", name: { kind: "Name", value: "logExpression" } }, - { - kind: "Field", - name: { kind: "Name", value: "dispatcher" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - ], - }, - }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const DeleteNotificationRuleDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "DeleteNotificationRule" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "Int" } } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "deleteNotificationRule" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "id" }, - value: { kind: "Variable", name: { kind: "Name", value: "id" } }, - }, - ], - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const CreateDispatcherDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "CreateDispatcher" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "DispatcherInput" } } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "createDispatcher" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const UpdateDispatcherDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "UpdateDispatcher" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "Int" } } }, - }, - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "DispatcherInput" } } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "updateDispatcher" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "id" }, - value: { kind: "Variable", name: { kind: "Name", value: "id" } }, - }, - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const DeleteDispatcherDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "DeleteDispatcher" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "Int" } } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "deleteDispatcher" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "id" }, - value: { kind: "Variable", name: { kind: "Name", value: "id" } }, - }, - ], - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const PreviewExpressionDocument = { - kind: "Document", - definitions: [ - { - kind: "OperationDefinition", - operation: "mutation", - name: { kind: "Name", value: "PreviewExpression" }, - variableDefinitions: [ - { - kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "input" } }, - type: { kind: "NonNullType", type: { kind: "NamedType", name: { kind: "Name", value: "PreviewInput" } } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "previewExpression" }, - arguments: [ - { - kind: "Argument", - name: { kind: "Name", value: "input" }, - value: { kind: "Variable", name: { kind: "Name", value: "input" } }, - }, - ], - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "containerError" } }, - { kind: "Field", name: { kind: "Name", value: "logError" } }, - { - kind: "Field", - name: { kind: "Name", value: "matchedContainers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "image" } }, - { kind: "Field", name: { kind: "Name", value: "host" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "matchedLogs" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "type" } }, - { kind: "Field", name: { kind: "Name", value: "message" } }, - { kind: "Field", name: { kind: "Name", value: "timestamp" } }, - { kind: "Field", name: { kind: "Name", value: "level" } }, - { kind: "Field", name: { kind: "Name", value: "stream" } }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "totalLogs" } }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; +export type PreviewExpressionMutation = { __typename?: 'Mutation', previewExpression: { __typename?: 'PreviewResult', containerError?: string | null, logError?: string | null, totalLogs: number, matchedContainers: Array<{ __typename?: 'Container', id: string, name: string, image: string, host?: string | null }>, matchedLogs: Array<{ __typename?: 'LogEvent', id: number, type?: string | null, message?: unknown | null, timestamp: number, level?: string | null, stream?: string | null }> } }; + +export type GetReleasesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetReleasesQuery = { __typename?: 'Query', releases: Array<{ __typename?: 'Release', name: string, mentionsCount: number, tag: string, body: string, createdAt: string, htmlUrl: string, latest: boolean, features: number, bugFixes: number, breaking: number }> }; + + +export const GetNotificationRulesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetNotificationRules"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"notificationRules"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"containerExpression"}},{"kind":"Field","name":{"kind":"Name","value":"logExpression"}},{"kind":"Field","name":{"kind":"Name","value":"triggerCount"}},{"kind":"Field","name":{"kind":"Name","value":"triggeredContainers"}},{"kind":"Field","name":{"kind":"Name","value":"lastTriggeredAt"}},{"kind":"Field","name":{"kind":"Name","value":"dispatcher"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetDispatchersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDispatchers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dispatchers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]} as unknown as DocumentNode; +export const CreateNotificationRuleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNotificationRule"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationRuleInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createNotificationRule"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"containerExpression"}},{"kind":"Field","name":{"kind":"Name","value":"logExpression"}},{"kind":"Field","name":{"kind":"Name","value":"dispatcher"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpdateNotificationRuleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateNotificationRule"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationRuleUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateNotificationRule"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"containerExpression"}},{"kind":"Field","name":{"kind":"Name","value":"logExpression"}},{"kind":"Field","name":{"kind":"Name","value":"dispatcher"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]} as unknown as DocumentNode; +export const ReplaceNotificationRuleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ReplaceNotificationRule"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"NotificationRuleInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"replaceNotificationRule"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"containerExpression"}},{"kind":"Field","name":{"kind":"Name","value":"logExpression"}},{"kind":"Field","name":{"kind":"Name","value":"dispatcher"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}}]} as unknown as DocumentNode; +export const DeleteNotificationRuleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteNotificationRule"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteNotificationRule"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode; +export const CreateDispatcherDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateDispatcher"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DispatcherInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createDispatcher"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]} as unknown as DocumentNode; +export const UpdateDispatcherDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateDispatcher"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DispatcherInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateDispatcher"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}}]}}]} as unknown as DocumentNode; +export const DeleteDispatcherDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteDispatcher"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteDispatcher"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}]}}]} as unknown as DocumentNode; +export const PreviewExpressionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PreviewExpression"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PreviewInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"previewExpression"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"containerError"}},{"kind":"Field","name":{"kind":"Name","value":"logError"}},{"kind":"Field","name":{"kind":"Name","value":"matchedContainers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"image"}},{"kind":"Field","name":{"kind":"Name","value":"host"}}]}},{"kind":"Field","name":{"kind":"Name","value":"matchedLogs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"level"}},{"kind":"Field","name":{"kind":"Name","value":"stream"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalLogs"}}]}}]}}]} as unknown as DocumentNode; +export const GetReleasesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetReleases"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"releases"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"mentionsCount"}},{"kind":"Field","name":{"kind":"Name","value":"tag"}},{"kind":"Field","name":{"kind":"Name","value":"body"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"htmlUrl"}},{"kind":"Field","name":{"kind":"Name","value":"latest"}},{"kind":"Field","name":{"kind":"Name","value":"features"}},{"kind":"Field","name":{"kind":"Name","value":"bugFixes"}},{"kind":"Field","name":{"kind":"Name","value":"breaking"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/gqlgen.yml b/gqlgen.yml index 88645d61..11b2d7a8 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -172,3 +172,6 @@ models: LogEvent: model: - github.com/amir20/dozzle/internal/container.LogEvent + Release: + model: + - github.com/amir20/dozzle/internal/releases.Release diff --git a/graph/generated.go b/graph/generated.go index 9ecdb795..3dd73f56 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -17,6 +17,7 @@ import ( "github.com/99designs/gqlgen/graphql/introspection" "github.com/amir20/dozzle/graph/model" "github.com/amir20/dozzle/internal/container" + "github.com/amir20/dozzle/internal/releases" gqlparser "github.com/vektah/gqlparser/v2" "github.com/vektah/gqlparser/v2/ast" ) @@ -45,6 +46,7 @@ type ResolverRoot interface { LogEvent() LogEventResolver Mutation() MutationResolver Query() QueryResolver + Release() ReleaseResolver } type DirectiveRoot struct { @@ -118,6 +120,20 @@ type ComplexityRoot struct { Dispatchers func(childComplexity int) int NotificationRule func(childComplexity int, id int32) int NotificationRules func(childComplexity int) int + Releases func(childComplexity int) int + } + + Release struct { + Body func(childComplexity int) int + Breaking func(childComplexity int) int + BugFixes func(childComplexity int) int + CreatedAt func(childComplexity int) int + Features func(childComplexity int) int + HtmlUrl func(childComplexity int) int + Latest func(childComplexity int) int + MentionsCount func(childComplexity int) int + Name func(childComplexity int) int + Tag func(childComplexity int) int } } @@ -143,6 +159,14 @@ type QueryResolver interface { NotificationRule(ctx context.Context, id int32) (*model.NotificationRule, error) Dispatchers(ctx context.Context) ([]*model.Dispatcher, error) Dispatcher(ctx context.Context, id int32) (*model.Dispatcher, error) + Releases(ctx context.Context) ([]*releases.Release, error) +} +type ReleaseResolver interface { + MentionsCount(ctx context.Context, obj *releases.Release) (int32, error) + + Features(ctx context.Context, obj *releases.Release) (int32, error) + BugFixes(ctx context.Context, obj *releases.Release) (int32, error) + Breaking(ctx context.Context, obj *releases.Release) (int32, error) } type executableSchema struct { @@ -508,6 +532,73 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Query.NotificationRules(childComplexity), true + case "Query.releases": + if e.complexity.Query.Releases == nil { + break + } + + return e.complexity.Query.Releases(childComplexity), true + + case "Release.body": + if e.complexity.Release.Body == nil { + break + } + + return e.complexity.Release.Body(childComplexity), true + case "Release.breaking": + if e.complexity.Release.Breaking == nil { + break + } + + return e.complexity.Release.Breaking(childComplexity), true + case "Release.bugFixes": + if e.complexity.Release.BugFixes == nil { + break + } + + return e.complexity.Release.BugFixes(childComplexity), true + case "Release.createdAt": + if e.complexity.Release.CreatedAt == nil { + break + } + + return e.complexity.Release.CreatedAt(childComplexity), true + case "Release.features": + if e.complexity.Release.Features == nil { + break + } + + return e.complexity.Release.Features(childComplexity), true + case "Release.htmlUrl": + if e.complexity.Release.HtmlUrl == nil { + break + } + + return e.complexity.Release.HtmlUrl(childComplexity), true + case "Release.latest": + if e.complexity.Release.Latest == nil { + break + } + + return e.complexity.Release.Latest(childComplexity), true + case "Release.mentionsCount": + if e.complexity.Release.MentionsCount == nil { + break + } + + return e.complexity.Release.MentionsCount(childComplexity), true + case "Release.name": + if e.complexity.Release.Name == nil { + break + } + + return e.complexity.Release.Name(childComplexity), true + case "Release.tag": + if e.complexity.Release.Tag == nil { + break + } + + return e.complexity.Release.Tag(childComplexity), true } return 0, false @@ -2539,6 +2630,57 @@ func (ec *executionContext) fieldContext_Query_dispatcher(ctx context.Context, f return fc, nil } +func (ec *executionContext) _Query_releases(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Query_releases, + func(ctx context.Context) (any, error) { + return ec.resolvers.Query().Releases(ctx) + }, + nil, + ec.marshalNRelease2ᚕᚖgithubᚗcomᚋamir20ᚋdozzleᚋinternalᚋreleasesᚐReleaseᚄ, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Query_releases(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_Release_name(ctx, field) + case "mentionsCount": + return ec.fieldContext_Release_mentionsCount(ctx, field) + case "tag": + return ec.fieldContext_Release_tag(ctx, field) + case "body": + return ec.fieldContext_Release_body(ctx, field) + case "createdAt": + return ec.fieldContext_Release_createdAt(ctx, field) + case "htmlUrl": + return ec.fieldContext_Release_htmlUrl(ctx, field) + case "latest": + return ec.fieldContext_Release_latest(ctx, field) + case "features": + return ec.fieldContext_Release_features(ctx, field) + case "bugFixes": + return ec.fieldContext_Release_bugFixes(ctx, field) + case "breaking": + return ec.fieldContext_Release_breaking(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Release", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -2647,6 +2789,296 @@ func (ec *executionContext) fieldContext_Query___schema(_ context.Context, field return fc, nil } +func (ec *executionContext) _Release_name(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_name, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_mentionsCount(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_mentionsCount, + func(ctx context.Context) (any, error) { + return ec.resolvers.Release().MentionsCount(ctx, obj) + }, + nil, + ec.marshalNInt2int32, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_mentionsCount(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_tag(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_tag, + func(ctx context.Context) (any, error) { + return obj.Tag, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_tag(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_body(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_body, + func(ctx context.Context) (any, error) { + return obj.Body, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_body(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_createdAt(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_createdAt, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + ec.marshalNTime2timeᚐTime, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_htmlUrl(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_htmlUrl, + func(ctx context.Context) (any, error) { + return obj.HtmlUrl, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_htmlUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_latest(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_latest, + func(ctx context.Context) (any, error) { + return obj.Latest, nil + }, + nil, + ec.marshalNBoolean2bool, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_latest(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_features(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_features, + func(ctx context.Context) (any, error) { + return ec.resolvers.Release().Features(ctx, obj) + }, + nil, + ec.marshalNInt2int32, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_features(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_bugFixes(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_bugFixes, + func(ctx context.Context) (any, error) { + return ec.resolvers.Release().BugFixes(ctx, obj) + }, + nil, + ec.marshalNInt2int32, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_bugFixes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Release_breaking(ctx context.Context, field graphql.CollectedField, obj *releases.Release) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Release_breaking, + func(ctx context.Context) (any, error) { + return ec.resolvers.Release().Breaking(ctx, obj) + }, + nil, + ec.marshalNInt2int32, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Release_breaking(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Release", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -4885,6 +5317,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "releases": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_releases(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "__type": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { @@ -4917,6 +5371,214 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return out } +var releaseImplementors = []string{"Release"} + +func (ec *executionContext) _Release(ctx context.Context, sel ast.SelectionSet, obj *releases.Release) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, releaseImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Release") + case "name": + out.Values[i] = ec._Release_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "mentionsCount": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Release_mentionsCount(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "tag": + out.Values[i] = ec._Release_tag(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "body": + out.Values[i] = ec._Release_body(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "createdAt": + out.Values[i] = ec._Release_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "htmlUrl": + out.Values[i] = ec._Release_htmlUrl(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "latest": + out.Values[i] = ec._Release_latest(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "features": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Release_features(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "bugFixes": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Release_bugFixes(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "breaking": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Release_breaking(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var __DirectiveImplementors = []string{"__Directive"} func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { @@ -5574,6 +6236,60 @@ func (ec *executionContext) marshalNPreviewResult2ᚖgithubᚗcomᚋamir20ᚋdoz return ec._PreviewResult(ctx, sel, v) } +func (ec *executionContext) marshalNRelease2ᚕᚖgithubᚗcomᚋamir20ᚋdozzleᚋinternalᚋreleasesᚐReleaseᚄ(ctx context.Context, sel ast.SelectionSet, v []*releases.Release) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNRelease2ᚖgithubᚗcomᚋamir20ᚋdozzleᚋinternalᚋreleasesᚐRelease(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNRelease2ᚖgithubᚗcomᚋamir20ᚋdozzleᚋinternalᚋreleasesᚐRelease(ctx context.Context, sel ast.SelectionSet, v *releases.Release) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Release(ctx, sel, v) +} + func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) { res, err := graphql.UnmarshalString(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/graph/resolver.go b/graph/resolver.go index 134942f3..2858d103 100644 --- a/graph/resolver.go +++ b/graph/resolver.go @@ -6,6 +6,7 @@ import ( "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/notification" "github.com/amir20/dozzle/internal/notification/dispatcher" + "github.com/amir20/dozzle/internal/releases" container_support "github.com/amir20/dozzle/internal/support/container" ) @@ -32,6 +33,9 @@ type HostService interface { FindContainer(host string, id string, labels container.ContainerLabels) (*container_support.ContainerService, error) } +type ReleasesFetcher func() ([]releases.Release, error) + type Resolver struct { - HostService HostService + HostService HostService + ReleasesFetcher ReleasesFetcher } diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 0176351b..f9f72940 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -56,6 +56,19 @@ type PreviewResult { totalLogs: Int! } +type Release { + name: String! + mentionsCount: Int! + tag: String! + body: String! + createdAt: Time! + htmlUrl: String! + latest: Boolean! + features: Int! + bugFixes: Int! + breaking: Int! +} + # Inputs input NotificationRuleInput { name: String! @@ -90,6 +103,7 @@ type Query { notificationRule(id: Int!): NotificationRule dispatchers: [Dispatcher!]! dispatcher(id: Int!): Dispatcher + releases: [Release!]! } # Mutations diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index 90d11e2f..4eef77e8 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -13,6 +13,7 @@ import ( "github.com/amir20/dozzle/internal/container" "github.com/amir20/dozzle/internal/notification" "github.com/amir20/dozzle/internal/notification/dispatcher" + "github.com/amir20/dozzle/internal/releases" "github.com/expr-lang/expr" "github.com/expr-lang/expr/vm" ) @@ -312,6 +313,43 @@ func (r *queryResolver) Dispatcher(ctx context.Context, id int32) (*model.Dispat return nil, nil } +// Releases is the resolver for the releases field. +func (r *queryResolver) Releases(ctx context.Context) ([]*releases.Release, error) { + if r.ReleasesFetcher == nil { + return nil, nil + } + result, err := r.ReleasesFetcher() + if err != nil { + return nil, err + } + // Convert to pointers + releases := make([]*releases.Release, len(result)) + for i := range result { + releases[i] = &result[i] + } + return releases, nil +} + +// MentionsCount is the resolver for the mentionsCount field. +func (r *releaseResolver) MentionsCount(ctx context.Context, obj *releases.Release) (int32, error) { + return int32(obj.MentionsCount), nil +} + +// Features is the resolver for the features field. +func (r *releaseResolver) Features(ctx context.Context, obj *releases.Release) (int32, error) { + return int32(obj.Features), nil +} + +// BugFixes is the resolver for the bugFixes field. +func (r *releaseResolver) BugFixes(ctx context.Context, obj *releases.Release) (int32, error) { + return int32(obj.BugFixes), nil +} + +// Breaking is the resolver for the breaking field. +func (r *releaseResolver) Breaking(ctx context.Context, obj *releases.Release) (int32, error) { + return int32(obj.Breaking), nil +} + // Container returns ContainerResolver implementation. func (r *Resolver) Container() ContainerResolver { return &containerResolver{r} } @@ -324,7 +362,11 @@ func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } // Query returns QueryResolver implementation. func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } +// Release returns ReleaseResolver implementation. +func (r *Resolver) Release() ReleaseResolver { return &releaseResolver{r} } + type containerResolver struct{ *Resolver } type logEventResolver struct{ *Resolver } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } +type releaseResolver struct{ *Resolver } diff --git a/internal/web/graphql.go b/internal/web/graphql.go index e5cf736a..22c081a9 100644 --- a/internal/web/graphql.go +++ b/internal/web/graphql.go @@ -2,16 +2,24 @@ package web import ( "net/http" + "time" gqlhandler "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/amir20/dozzle/graph" + "github.com/amir20/dozzle/internal/cache" + "github.com/amir20/dozzle/internal/releases" ) func (h *handler) graphqlHandler() http.Handler { + releasesCache := cache.New(func() ([]releases.Release, error) { + return releases.Fetch(h.config.Version) + }, time.Hour) + srv := gqlhandler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{ Resolvers: &graph.Resolver{ - HostService: h.hostService, + HostService: h.hostService, + ReleasesFetcher: releasesCache.Get, }, })) diff --git a/internal/web/releases.go b/internal/web/releases.go deleted file mode 100644 index 927e4e16..00000000 --- a/internal/web/releases.go +++ /dev/null @@ -1,38 +0,0 @@ -package web - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/amir20/dozzle/internal/cache" - "github.com/amir20/dozzle/internal/releases" - "github.com/rs/zerolog/log" -) - -var cachedReleases *cache.Cache[[]releases.Release] - -func (h *handler) releases(w http.ResponseWriter, r *http.Request) { - if cachedReleases == nil { - cachedReleases = cache.New(func() ([]releases.Release, error) { - return releases.Fetch(h.config.Version) - }, time.Hour) - } - releases, err, hit := cachedReleases.GetWithHit() - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - log.Debug().Err(err).Msg("error fetching releases") - return - } - - w.Header().Set("Content-Type", "application/json") - if hit { - w.Header().Set("X-Cache", "HIT") - } - - if err := json.NewEncoder(w).Encode(releases); err != nil { - log.Error().Err(err).Msg("error encoding releases") - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} diff --git a/internal/web/routes.go b/internal/web/routes.go index 880d7d68..dfef9ec8 100644 --- a/internal/web/routes.go +++ b/internal/web/routes.go @@ -149,7 +149,6 @@ func createRouter(h *handler) *chi.Mux { r.Get("/hosts/{host}/containers/{id}/exec", h.exec) } - r.Get("/releases", h.releases) if !h.config.DisableAvatars { r.Get("/profile/avatar", h.avatar) }