Skip to content

Commit acb90a2

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(selectors): support legacy BigQuery project ids
1 parent dd96bf7 commit acb90a2

2 files changed

Lines changed: 38 additions & 16 deletions

File tree

apps/sim/lib/selectors/server/providers/bigquery.test.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ import type { ExecuteServerSelectorArgs } from '@/lib/selectors/server/types'
1919
const PROJECT_ID = 'selector-test'
2020
const DATASET_ID = 'analytics'
2121

22+
interface DatasetFixture {
23+
datasetReference: { projectId: string; datasetId: string }
24+
friendlyName: string
25+
}
26+
27+
interface TableFixture {
28+
tableReference: { projectId: string; datasetId: string; tableId: string }
29+
friendlyName: string
30+
}
31+
2232
function args(
2333
selectorKey: 'bigquery.datasets' | 'bigquery.tables',
2434
request: ExecuteServerSelectorArgs['request']
@@ -41,14 +51,14 @@ function args(
4151
}
4252
}
4353

44-
function dataset(datasetId: string, friendlyName: string) {
54+
function dataset(datasetId: string, friendlyName: string, projectId = PROJECT_ID): DatasetFixture {
4555
return {
46-
datasetReference: { projectId: PROJECT_ID, datasetId },
56+
datasetReference: { projectId, datasetId },
4757
friendlyName,
4858
}
4959
}
5060

51-
function table(tableId: string, friendlyName: string) {
61+
function table(tableId: string, friendlyName: string): TableFixture {
5262
return {
5363
tableReference: { projectId: PROJECT_ID, datasetId: DATASET_ID, tableId },
5464
friendlyName,
@@ -150,22 +160,28 @@ describe('BigQuery server selector adapters', () => {
150160
expect(mockFetch).toHaveBeenCalledTimes(2)
151161
})
152162

153-
it('hydrates a selected dataset directly by id', async () => {
163+
it('hydrates a selected dataset in a legacy domain-scoped project directly by id', async () => {
164+
const projectId = 'example.com:selector-test'
154165
mockFetch.mockResolvedValueOnce(
155-
new Response(JSON.stringify(dataset('saved_dataset', 'Saved Dataset')), { status: 200 })
166+
new Response(JSON.stringify(dataset('saved_dataset', 'Saved Dataset', projectId)), {
167+
status: 200,
168+
})
156169
)
157170

171+
const detailArgs = args('bigquery.datasets', { kind: 'detail', id: 'saved_dataset' })
172+
detailArgs.context.projectId = projectId
173+
158174
await expect(
159-
bigQuerySelectorAttachments['bigquery.datasets'].execute(
160-
args('bigquery.datasets', { kind: 'detail', id: 'saved_dataset' })
161-
)
175+
bigQuerySelectorAttachments['bigquery.datasets'].execute(detailArgs)
162176
).resolves.toEqual({
163177
kind: 'detail',
164178
item: { id: 'saved_dataset', label: 'Saved Dataset' },
165179
})
166180

167181
const url = new URL(String(mockFetch.mock.calls[0]?.[0]))
168-
expect(url.pathname).toBe(`/bigquery/v2/projects/${PROJECT_ID}/datasets/saved_dataset`)
182+
expect(url.pathname).toBe(
183+
'/bigquery/v2/projects/example.com%3Aselector-test/datasets/saved_dataset'
184+
)
169185
expect(url.searchParams.get('datasetView')).toBe('METADATA')
170186
expect(mockFetch).toHaveBeenCalledTimes(1)
171187
})

apps/sim/lib/selectors/server/providers/bigquery.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ import {
1717
listSelectorResult,
1818
requireListRequest,
1919
type ServerSelectorAttachmentMap,
20+
type ServerSelectorExecutionResult,
2021
} from '@/lib/selectors/server/types'
2122

2223
type BigQuerySelectorKey = Extract<ServerSelectorKey, 'bigquery.datasets' | 'bigquery.tables'>
2324

2425
const BIGQUERY_PAGE_SIZE = 200
2526
const BIGQUERY_CURSOR_MAX_LENGTH = 4_096
2627
const BIGQUERY_SCOPES = getScopesForService('google-bigquery')
27-
const PROJECT_ID_PATTERN = /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/
28+
/** Standard project IDs plus Google's legacy `domain.tld:project-id` form. */
29+
const PROJECT_ID_PATTERN = /^([a-z][a-z0-9.-]{0,61}[a-z0-9]:)?[a-z][a-z0-9-]{4,28}[a-z0-9]$/
2830
const DATASET_ID_PATTERN = /^[A-Za-z0-9_]{1,1024}$/
2931
const TABLE_ID_PATTERN = /^[\p{L}\p{M}\p{N}\p{Pc}\p{Pd}\p{Zs}]+$/u
3032

@@ -67,7 +69,9 @@ const tablesPageSchema = z.object({
6769
nextPageToken: z.string().min(1).max(4_096).optional(),
6870
})
6971

70-
function requireCredential(args: ExecuteServerSelectorArgs) {
72+
function requireCredential(
73+
args: ExecuteServerSelectorArgs
74+
): NonNullable<ExecuteServerSelectorArgs['credential']> {
7175
if (!args.credential) throw new SelectorConnectionUnavailableError()
7276
return args.credential
7377
}
@@ -108,14 +112,16 @@ async function getAccessToken(args: ExecuteServerSelectorArgs): Promise<string>
108112
})
109113
}
110114

111-
function requestHeaders(accessToken: string) {
115+
function requestHeaders(accessToken: string): Record<string, string> {
112116
return {
113117
Authorization: `Bearer ${accessToken}`,
114118
Accept: 'application/json',
115119
}
116120
}
117121

118-
async function listDatasets(args: ExecuteServerSelectorArgs) {
122+
async function listDatasets(
123+
args: ExecuteServerSelectorArgs
124+
): Promise<ServerSelectorExecutionResult> {
119125
const request = requireListRequest(args.selectorKey, args.request)
120126
const projectId = requireProjectId(args.context.projectId)
121127
const cursor = requireCursor(request.cursor)
@@ -146,7 +152,7 @@ async function listDatasets(args: ExecuteServerSelectorArgs) {
146152
)
147153
}
148154

149-
async function listTables(args: ExecuteServerSelectorArgs) {
155+
async function listTables(args: ExecuteServerSelectorArgs): Promise<ServerSelectorExecutionResult> {
150156
const request = requireListRequest(args.selectorKey, args.request)
151157
const projectId = requireProjectId(args.context.projectId)
152158
const datasetId = requireDatasetId(args.context.datasetId)
@@ -183,7 +189,7 @@ async function listTables(args: ExecuteServerSelectorArgs) {
183189
)
184190
}
185191

186-
async function getDataset(args: ExecuteServerSelectorArgs) {
192+
async function getDataset(args: ExecuteServerSelectorArgs): Promise<ServerSelectorExecutionResult> {
187193
if (args.request.kind !== 'detail') throw new SelectorOptionsUnavailableError()
188194
const projectId = requireProjectId(args.context.projectId)
189195
const datasetId = requireDatasetId(args.request.id)
@@ -218,7 +224,7 @@ async function getDataset(args: ExecuteServerSelectorArgs) {
218224
})
219225
}
220226

221-
async function getTable(args: ExecuteServerSelectorArgs) {
227+
async function getTable(args: ExecuteServerSelectorArgs): Promise<ServerSelectorExecutionResult> {
222228
if (args.request.kind !== 'detail') throw new SelectorOptionsUnavailableError()
223229
const projectId = requireProjectId(args.context.projectId)
224230
const datasetId = requireDatasetId(args.context.datasetId)

0 commit comments

Comments
 (0)