@@ -49,6 +49,8 @@ export interface DataSource {
49
49
export interface DataSourceData < T = any > {
50
50
dataSource : DataSource ;
51
51
data : T ;
52
+ /** Optional list of column headers (for gsheet sources). */
53
+ headers ?: string [ ] ;
52
54
}
53
55
54
56
export async function addDataSource (
@@ -157,7 +159,7 @@ export async function syncDataSource(id: string) {
157
159
throw new Error ( `sync failed: ${ err } ` ) ;
158
160
}
159
161
} else {
160
- const data = await fetchData ( dataSource ) ;
162
+ const { data, headers } = await fetchData ( dataSource ) ;
161
163
162
164
const projectId = window . __ROOT_CTX . rootConfig . projectId ;
163
165
const db = window . firebase . db ;
@@ -183,6 +185,7 @@ export async function syncDataSource(id: string) {
183
185
batch . set ( dataDocRef , {
184
186
dataSource : updatedDataSource ,
185
187
data : data ,
188
+ ...( headers ? { headers} : { } ) ,
186
189
} ) ;
187
190
batch . update ( dataSourceDocRef , {
188
191
syncedAt : Timestamp . now ( ) ,
@@ -235,6 +238,7 @@ export async function publishDataSource(id: string) {
235
238
batch . set ( dataDocRefPublished , {
236
239
dataSource : updatedDataSource ,
237
240
data : dataRes ?. data || null ,
241
+ ...( dataRes ?. headers ? { headers : dataRes . headers } : { } ) ,
238
242
} ) ;
239
243
batch . update ( dataDocRefDraft , {
240
244
dataSource : updatedDataSource ,
@@ -280,17 +284,23 @@ export async function deleteDataSource(id: string) {
280
284
logAction ( 'datasource.delete' , { metadata : { datasourceId : id } } ) ;
281
285
}
282
286
283
- async function fetchData ( dataSource : DataSource ) {
287
+ interface FetchedData {
288
+ data : any ;
289
+ headers ?: string [ ] ;
290
+ }
291
+
292
+ async function fetchData ( dataSource : DataSource ) : Promise < FetchedData > {
284
293
if ( dataSource . type === 'http' ) {
285
- return await fetchHttpData ( dataSource ) ;
294
+ const data = await fetchHttpData ( dataSource ) ;
295
+ return { data} ;
286
296
}
287
297
if ( dataSource . type === 'gsheet' ) {
288
298
return await fetchGsheetData ( dataSource ) ;
289
299
}
290
300
throw new Error ( `unsupported data source: ${ dataSource . type } ` ) ;
291
301
}
292
302
293
- async function fetchGsheetData ( dataSource : DataSource ) {
303
+ async function fetchGsheetData ( dataSource : DataSource ) : Promise < FetchedData > {
294
304
const gsheetId = parseSpreadsheetUrl ( dataSource . url ) ;
295
305
if ( ! gsheetId ?. spreadsheetId ) {
296
306
throw new Error ( `failed to parse google sheet url: ${ dataSource . url } ` ) ;
@@ -303,10 +313,21 @@ async function fetchGsheetData(dataSource: DataSource) {
303
313
}
304
314
305
315
const dataFormat = dataSource . dataFormat || 'map' ;
316
+ const [ headers , rows ] = await gsheet . getValues ( ) ;
306
317
if ( dataFormat === 'array' ) {
307
- return await gsheet . getValues ( ) ;
318
+ return { data : [ headers , rows ] , headers } ;
308
319
}
309
- return await gsheet . getValuesMap ( ) ;
320
+ const mapData = rows . map ( ( row ) => {
321
+ const item : Record < string , string > = { } ;
322
+ row . forEach ( ( val , i ) => {
323
+ const key = headers [ i ] ;
324
+ if ( key ) {
325
+ item [ key ] = String ( val || '' ) ;
326
+ }
327
+ } ) ;
328
+ return item ;
329
+ } ) ;
330
+ return { data : mapData , headers} ;
310
331
}
311
332
312
333
async function fetchHttpData ( dataSource : DataSource ) {
0 commit comments