Skip to content

Commit

Permalink
Merge pull request #4630 from Shopify/filter-app-results-on-app-title
Browse files Browse the repository at this point in the history
Filter app results by title
  • Loading branch information
amcaplan authored Nov 24, 2024
2 parents 95828d0 + c55df5d commit e56fad4
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 39 deletions.
106 changes: 81 additions & 25 deletions packages/app/src/cli/api/graphql/app-management/generated/apps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,32 @@ import {JsonMapType} from '@shopify/cli-kit/node/toml'

import {TypedDocumentNode as DocumentNode} from '@graphql-typed-document-node/core'

export type ListAppsQueryVariables = Types.Exact<{[key: string]: never}>
export type ListAppsQueryVariables = Types.Exact<{
query?: Types.InputMaybe<Types.Scalars['String']['input']>
}>

export type ListAppsQuery = {
apps: {
id: string
key: string
activeRelease: {
id: string
version: {
name: string
appModules: {
uuid: string
handle: string
config: JsonMapType
specification: {identifier: string; externalIdentifier: string; name: string}
}[]
appsConnection?: {
edges: {
node: {
id: string
key: string
activeRelease: {
id: string
version: {
name: string
appModules: {
uuid: string
handle: string
config: JsonMapType
specification: {identifier: string; externalIdentifier: string; name: string}
}[]
}
}
}
}
}[]
}[]
pageInfo: {hasNextPage: boolean}
} | null
}

export const ListApps = {
Expand All @@ -32,38 +39,76 @@ export const ListApps = {
kind: 'OperationDefinition',
operation: 'query',
name: {kind: 'Name', value: 'listApps'},
variableDefinitions: [
{
kind: 'VariableDefinition',
variable: {kind: 'Variable', name: {kind: 'Name', value: 'query'}},
type: {kind: 'NamedType', name: {kind: 'Name', value: 'String'}},
},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {kind: 'Name', value: 'apps'},
name: {kind: 'Name', value: 'appsConnection'},
arguments: [
{
kind: 'Argument',
name: {kind: 'Name', value: 'query'},
value: {kind: 'Variable', name: {kind: 'Name', value: 'query'}},
},
{kind: 'Argument', name: {kind: 'Name', value: 'first'}, value: {kind: 'IntValue', value: '50'}},
],
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{kind: 'Field', name: {kind: 'Name', value: 'key'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'activeRelease'},
name: {kind: 'Name', value: 'edges'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'version'},
name: {kind: 'Name', value: 'node'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{kind: 'Field', name: {kind: 'Name', value: 'key'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'appModules'},
name: {kind: 'Name', value: 'activeRelease'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'FragmentSpread', name: {kind: 'Name', value: 'ReleasedAppModule'}},
{kind: 'Field', name: {kind: 'Name', value: 'id'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'version'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'name'}},
{
kind: 'Field',
name: {kind: 'Name', value: 'appModules'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'FragmentSpread',
name: {kind: 'Name', value: 'ReleasedAppModule'},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
Expand All @@ -76,6 +121,17 @@ export const ListApps = {
],
},
},
{
kind: 'Field',
name: {kind: 'Name', value: 'pageInfo'},
selectionSet: {
kind: 'SelectionSet',
selections: [
{kind: 'Field', name: {kind: 'Name', value: 'hasNextPage'}},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
},
{kind: 'Field', name: {kind: 'Name', value: '__typename'}},
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
query listApps {
apps {
id
key
activeRelease {
id
version {
name
appModules {
...ReleasedAppModule
query listApps($query: String) {
appsConnection(query: $query, first: 50) {
edges {
node {
id
key
activeRelease {
id
version {
name
appModules {
...ReleasedAppModule
}
}
}
}
}
pageInfo {
hasNextPage
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import {
import {OrganizationBetaFlagsQuerySchema} from './app-management-client/graphql/organization_beta_flags.js'
import {testUIExtension, testRemoteExtensionTemplates, testOrganizationApp} from '../../models/app/app.test-data.js'
import {ExtensionInstance} from '../../models/extensions/extension-instance.js'
import {ListApps} from '../../api/graphql/app-management/generated/apps.js'
import {describe, expect, test, vi} from 'vitest'
import {CLI_KIT_VERSION} from '@shopify/cli-kit/common/version'
import {fetch} from '@shopify/cli-kit/node/http'
import {businessPlatformOrganizationsRequest} from '@shopify/cli-kit/node/api/business-platform'
import {appManagementRequestDoc} from '@shopify/cli-kit/node/api/app-management'
import {BugError} from '@shopify/cli-kit/node/error'

vi.mock('@shopify/cli-kit/node/http')
vi.mock('@shopify/cli-kit/node/api/business-platform')
vi.mock('@shopify/cli-kit/node/api/app-management')

const extensionA = await testUIExtension({uid: 'extension-a-uuid'})
const extensionB = await testUIExtension({uid: 'extension-b-uuid'})
Expand Down Expand Up @@ -187,3 +191,68 @@ describe('versionDeepLink', () => {
expect(got).toEqual('https://dev.shopify.com/dashboard/1/apps/2/versions/3')
})
})

describe('searching for apps', () => {
test.each([
['without a term if none is provided', undefined, ''],
['without a term if a blank string is provided', '', ''],
['with a single term passed in the query', 'test-app', 'title:test-app'],
['with multiple terms passed in the query', 'test app', 'title:test title:app'],
])('searches for apps by name %s', async (_: string, query: string | undefined, queryVariable: string) => {
// Given
const orgId = '1'
const appName = 'test-app'
const apps = [testOrganizationApp({title: appName})]
const mockedFetchAppsResponse = {
appsConnection: {
edges: apps.map((app, index) => ({
node: {
...app,
key: `key-${index}`,
activeRelease: {
id: 'gid://shopify/Release/1',
version: {
name: app.title,
appModules: [],
},
},
},
})),
pageInfo: {
hasNextPage: false,
},
},
}
vi.mocked(appManagementRequestDoc).mockResolvedValueOnce(mockedFetchAppsResponse)

// When
const client = new AppManagementClient()
client.token = () => Promise.resolve('token')
const got = await client.appsForOrg(orgId, query)

// Then
expect(vi.mocked(appManagementRequestDoc)).toHaveBeenCalledWith(orgId, ListApps, 'token', {query: queryVariable})
expect(got).toEqual({
apps: apps.map((app, index) => ({
apiKey: `key-${index}`,
id: app.id,
organizationId: app.organizationId,
title: app.title,
})),
hasMorePages: false,
})
})

test("Throws a BugError if the response doesn't contain the expected data", async () => {
// Given
const orgId = '1'
vi.mocked(appManagementRequestDoc).mockResolvedValueOnce({})

// When
const client = new AppManagementClient()
client.token = () => Promise.resolve('token')

// Then
await expect(client.appsForOrg(orgId)).rejects.toThrow(BugError)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,21 @@ export class AppManagementClient implements DeveloperPlatformClient {
return {organization: organization!, apps, hasMorePages}
}

async appsForOrg(organizationId: string, _term?: string): Promise<Paginateable<{apps: MinimalOrganizationApp[]}>> {
async appsForOrg(organizationId: string, term = ''): Promise<Paginateable<{apps: MinimalOrganizationApp[]}>> {
const query = ListApps
const result = await appManagementRequestDoc(organizationId, query, await this.token())
const minimalOrganizationApps = result.apps.map((app) => {
const variables = {
query: term
.split(' ')
.filter((word) => word)
.map((word) => `title:${word}`)
.join(' '),
}
const result = await appManagementRequestDoc(organizationId, query, await this.token(), variables)
if (!result.appsConnection) {
throw new BugError('Server failed to retrieve apps')
}
const minimalOrganizationApps = result.appsConnection.edges.map((edge) => {
const app = edge.node
return {
id: app.id,
apiKey: app.key,
Expand All @@ -270,7 +281,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
})
return {
apps: minimalOrganizationApps,
hasMorePages: false,
hasMorePages: result.appsConnection.pageInfo.hasNextPage,
}
}

Expand Down

0 comments on commit e56fad4

Please sign in to comment.