From b3c72450432bce78595399f3a3b3b8a79db7c925 Mon Sep 17 00:00:00 2001 From: Marcin Maciaszczyk Date: Mon, 4 Nov 2024 14:41:58 +0100 Subject: [PATCH] cluster apps --- www/src/components/cluster/ClusterApp.tsx | 4 +- www/src/components/cluster/ClusterApps.tsx | 19 ++--- www/src/components/cluster/queries.ts | 27 ------- www/src/generated/graphql.ts | 90 +++++++++++++++++++--- www/src/graph/oauth.graphql | 10 +-- www/src/graph/repos.graphql | 25 ++++++ 6 files changed, 118 insertions(+), 57 deletions(-) delete mode 100644 www/src/components/cluster/queries.ts diff --git a/www/src/components/cluster/ClusterApp.tsx b/www/src/components/cluster/ClusterApp.tsx index 7ec7d5f5ca..2fcf6a0dc3 100644 --- a/www/src/components/cluster/ClusterApp.tsx +++ b/www/src/components/cluster/ClusterApp.tsx @@ -12,11 +12,11 @@ import { useNavigate, useParams } from 'react-router-dom' import { useTheme } from 'styled-components' -import { Repository } from '../../generated/graphql' +import { Repository, RepositoryFragment } from '../../generated/graphql' import { MoreMenu } from '../account/MoreMenu' type ClusterAppProps = { - app: Repository + app: RepositoryFragment consoleUrl?: string | null last: boolean } diff --git a/www/src/components/cluster/ClusterApps.tsx b/www/src/components/cluster/ClusterApps.tsx index 651bd59296..d8b62022ed 100644 --- a/www/src/components/cluster/ClusterApps.tsx +++ b/www/src/components/cluster/ClusterApps.tsx @@ -4,15 +4,15 @@ import Fuse from 'fuse.js' import { Input, MagnifyingGlassIcon } from '@pluralsh/design-system' import ListCard from '../utils/ListCard' -import usePaginatedQuery from '../../hooks/usePaginatedQuery' import LoadingIndicator from '../utils/LoadingIndicator' import { EmptyListMessage } from '../overview/clusters/misc' import InfiniteScroller from '../utils/InfiniteScroller' -import { Cluster } from '../../generated/graphql' +import { Cluster, useRepositoriesQuery } from '../../generated/graphql' import { ensureURLValidity } from '../../utils/url' import { ClusterApp } from './ClusterApp' -import { REPOSITORIES_Q } from './queries' +import { useFetchPaginatedData } from '../utils/useFetchPaginatedData' +import { mapExistingNodes } from '../../utils/graphql' const searchOptions = { keys: ['name'], @@ -25,12 +25,13 @@ export function ClusterApps({ cluster: { consoleUrl }, }: ClusterAppsProps): ReactElement { const [search, setSearch] = useState('') - const [apps, loading, hasMore, loadMore] = usePaginatedQuery( - REPOSITORIES_Q, - { variables: { installed: true } }, - (data) => data.repositories + const { data, loading, pageInfo, fetchNextPage } = useFetchPaginatedData( + { queryHook: useRepositoriesQuery, keyPath: ['repositories'] }, + { installed: true } ) + const apps = useMemo(() => mapExistingNodes(data?.repositories) ?? [], [data]) + const fuse = useMemo(() => new Fuse(apps, searchOptions), [apps]) const filteredApps = useMemo( () => (search ? fuse.search(search).map(({ item }) => item) : apps), @@ -59,8 +60,8 @@ export function ClusterApps({ {!isEmpty(apps) ? ( ; @@ -5902,6 +5900,8 @@ export type FileContentFragment = { __typename?: 'FileContent', content: string, export type RepoFragment = { __typename?: 'Repository', id: string, name: string, notes?: string | null, description?: string | null, documentation?: string | null, icon?: string | null, darkIcon?: string | null, private?: boolean | null, trending?: boolean | null, verified?: boolean | null, category?: Category | null, docs?: Array<{ __typename?: 'FileContent', content: string, path: string } | null> | null, oauthSettings?: { __typename?: 'OauthSettings', uriFormat: string, authMethod: OidcAuthMethod } | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null, owner?: { __typename?: 'User', id: string, name: string, email: string, avatar?: string | null, provider?: Provider | null, demoed?: boolean | null, onboarding?: OnboardingState | null, emailConfirmed?: boolean | null, emailConfirmBy?: Date | null, backgroundColor?: string | null, serviceAccount?: boolean | null, hasInstallations?: boolean | null, hasShell?: boolean | null, onboardingChecklist?: { __typename?: 'OnboardingChecklist', dismissed?: boolean | null, status?: OnboardingChecklistState | null } | null, invites?: Array<{ __typename?: 'Invite', id: string, email?: string | null } | null> | null, roles?: { __typename?: 'Roles', admin?: boolean | null } | null, groups?: Array<{ __typename?: 'Group', id: string, name: string, global?: boolean | null, description?: string | null } | null> | null, impersonationPolicy?: { __typename?: 'ImpersonationPolicy', id: string, bindings?: Array<{ __typename?: 'ImpersonationPolicyBinding', id: string, group?: { __typename?: 'Group', id: string, name: string } | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null } | null> | null } | null } | null, address?: { __typename?: 'Address', line1?: string | null, line2?: string | null, city?: string | null, country?: string | null, state?: string | null, zip?: string | null } | null } | null, recipes?: Array<{ __typename?: 'Recipe', name: string, provider?: Provider | null, description?: string | null } | null> | null }; +export type RepositoryFragment = { __typename?: 'Repository', id: string, name: string, icon?: string | null, darkIcon?: string | null, installation?: { __typename?: 'Installation', pingedAt?: Date | null, synced?: boolean | null, locked?: boolean | null } | null }; + export type MarketplaceRepositoryFragment = { __typename?: 'Repository', id: string, name: string, description?: string | null, releaseStatus?: ReleaseStatus | null, documentation?: string | null, icon?: string | null, darkIcon?: string | null, private?: boolean | null, trending?: boolean | null, verified?: boolean | null, category?: Category | null, oauthSettings?: { __typename?: 'OauthSettings', uriFormat: string, authMethod: OidcAuthMethod } | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null, owner?: { __typename?: 'User', id: string, name: string, email: string, avatar?: string | null, provider?: Provider | null, demoed?: boolean | null, onboarding?: OnboardingState | null, emailConfirmed?: boolean | null, emailConfirmBy?: Date | null, backgroundColor?: string | null, serviceAccount?: boolean | null, hasInstallations?: boolean | null, hasShell?: boolean | null, onboardingChecklist?: { __typename?: 'OnboardingChecklist', dismissed?: boolean | null, status?: OnboardingChecklistState | null } | null, invites?: Array<{ __typename?: 'Invite', id: string, email?: string | null } | null> | null, roles?: { __typename?: 'Roles', admin?: boolean | null } | null, groups?: Array<{ __typename?: 'Group', id: string, name: string, global?: boolean | null, description?: string | null } | null> | null, impersonationPolicy?: { __typename?: 'ImpersonationPolicy', id: string, bindings?: Array<{ __typename?: 'ImpersonationPolicyBinding', id: string, group?: { __typename?: 'Group', id: string, name: string } | null, user?: { __typename?: 'User', id: string, name: string, email: string } | null } | null> | null } | null } | null, address?: { __typename?: 'Address', line1?: string | null, line2?: string | null, city?: string | null, country?: string | null, state?: string | null, zip?: string | null } | null } | null, installation?: { __typename?: 'Installation', id: string } | null, tags?: Array<{ __typename?: 'Tag', tag: string } | null> | null }; export type DependenciesFragment = { __typename?: 'Dependencies', wait?: boolean | null, application?: boolean | null, providers?: Array | null, secrets?: Array | null, providerWirings?: Map | null, outputs?: Map | null, dependencies?: Array<{ __typename?: 'Dependency', name?: string | null, repo?: string | null, type?: DependencyType | null, version?: string | null, optional?: boolean | null } | null> | null, wirings?: { __typename?: 'Wirings', terraform?: Map | null, helm?: Map | null } | null }; @@ -5971,6 +5971,14 @@ export type UnlockRepositoryMutationVariables = Exact<{ export type UnlockRepositoryMutation = { __typename?: 'RootMutationType', unlockRepository?: number | null }; +export type RepositoriesQueryVariables = Exact<{ + cursor?: InputMaybe; + installed?: InputMaybe; +}>; + + +export type RepositoriesQuery = { __typename?: 'RootQueryType', repositories?: { __typename?: 'RepositoryConnection', pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean }, edges?: Array<{ __typename?: 'RepositoryEdge', node?: { __typename?: 'Repository', id: string, name: string, icon?: string | null, darkIcon?: string | null, installation?: { __typename?: 'Installation', pingedAt?: Date | null, synced?: boolean | null, locked?: boolean | null } | null } | null } | null> | null } | null }; + export type MarketplaceRepositoriesQueryVariables = Exact<{ publisherId?: InputMaybe; tag?: InputMaybe; @@ -7255,13 +7263,6 @@ export const OAuthInfoFragmentDoc = gql` authorizeUrl } `; -export const RepositoryFragmentDoc = gql` - fragment Repository on Repository { - name - icon - darkIcon -} - `; export const SubscriptionFragmentDoc = gql` fragment Subscription on RepositorySubscription { id @@ -7590,6 +7591,19 @@ export const CategoryFragmentDoc = gql` count } `; +export const RepositoryFragmentDoc = gql` + fragment Repository on Repository { + id + name + icon + darkIcon + installation { + pingedAt + synced + locked + } +} + `; export const MarketplaceRepositoryFragmentDoc = gql` fragment MarketplaceRepository on Repository { id @@ -9319,7 +9333,9 @@ export const OidcConsentDocument = gql` query OIDCConsent($challenge: String!) { oidcConsent(challenge: $challenge) { repository { - ...Repository + name + icon + darkIcon } consent { requestedScope @@ -9327,7 +9343,7 @@ export const OidcConsentDocument = gql` } } } - ${RepositoryFragmentDoc}`; + `; /** * __useOidcConsentQuery__ @@ -10703,6 +10719,55 @@ export function useUnlockRepositoryMutation(baseOptions?: Apollo.MutationHookOpt export type UnlockRepositoryMutationHookResult = ReturnType; export type UnlockRepositoryMutationResult = Apollo.MutationResult; export type UnlockRepositoryMutationOptions = Apollo.BaseMutationOptions; +export const RepositoriesDocument = gql` + query Repositories($cursor: String, $installed: Boolean) { + repositories(after: $cursor, first: 200, installed: $installed) { + pageInfo { + ...PageInfo + } + edges { + node { + ...Repository + } + } + } +} + ${PageInfoFragmentDoc} +${RepositoryFragmentDoc}`; + +/** + * __useRepositoriesQuery__ + * + * To run a query within a React component, call `useRepositoriesQuery` and pass it any options that fit your needs. + * When your component renders, `useRepositoriesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRepositoriesQuery({ + * variables: { + * cursor: // value for 'cursor' + * installed: // value for 'installed' + * }, + * }); + */ +export function useRepositoriesQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(RepositoriesDocument, options); + } +export function useRepositoriesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(RepositoriesDocument, options); + } +export function useRepositoriesSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(RepositoriesDocument, options); + } +export type RepositoriesQueryHookResult = ReturnType; +export type RepositoriesLazyQueryHookResult = ReturnType; +export type RepositoriesSuspenseQueryHookResult = ReturnType; +export type RepositoriesQueryResult = Apollo.QueryResult; export const MarketplaceRepositoriesDocument = gql` query MarketplaceRepositories($publisherId: ID, $tag: String, $cursor: String) { repositories(publisherId: $publisherId, tag: $tag, after: $cursor, first: 200) { @@ -12486,6 +12551,7 @@ export const namedOperations = { GetStack: 'GetStack', ListStacks: 'ListStacks', Repository: 'Repository', + Repositories: 'Repositories', MarketplaceRepositories: 'MarketplaceRepositories', Scaffolds: 'Scaffolds', GetTfProviders: 'GetTfProviders', @@ -12620,7 +12686,6 @@ export const namedOperations = { NotificationFragment: 'NotificationFragment', OIDCProvider: 'OIDCProvider', OAuthInfo: 'OAuthInfo', - Repository: 'Repository', Limit: 'Limit', LineItem: 'LineItem', ServiceLevel: 'ServiceLevel', @@ -12646,6 +12711,7 @@ export const namedOperations = { Category: 'Category', FileContent: 'FileContent', Repo: 'Repo', + Repository: 'Repository', MarketplaceRepository: 'MarketplaceRepository', Dependencies: 'Dependencies', Integration: 'Integration', diff --git a/www/src/graph/oauth.graphql b/www/src/graph/oauth.graphql index 585bb48c09..82c0a6022b 100644 --- a/www/src/graph/oauth.graphql +++ b/www/src/graph/oauth.graphql @@ -33,16 +33,12 @@ fragment OAuthInfo on OauthInfo { authorizeUrl } -fragment Repository on Repository { - name - icon - darkIcon -} - query OIDCConsent($challenge: String!) { oidcConsent(challenge: $challenge) { repository { - ...Repository + name + icon + darkIcon } consent { requestedScope diff --git a/www/src/graph/repos.graphql b/www/src/graph/repos.graphql index e1f7b842aa..7c2a0b1eaa 100644 --- a/www/src/graph/repos.graphql +++ b/www/src/graph/repos.graphql @@ -42,6 +42,18 @@ fragment Repo on Repository { } } +fragment Repository on Repository { + id + name + icon + darkIcon + installation { + pingedAt + synced + locked + } +} + fragment MarketplaceRepository on Repository { id name @@ -195,6 +207,19 @@ mutation UnlockRepository($name: String!) { unlockRepository(name: $name) } +query Repositories($cursor: String, $installed: Boolean) { + repositories(after: $cursor, first: 200, installed: $installed) { + pageInfo { + ...PageInfo + } + edges { + node { + ...Repository + } + } + } +} + query MarketplaceRepositories($publisherId: ID, $tag: String, $cursor: String) { repositories( publisherId: $publisherId