import buildOpts from './buildOpts' ; import checkArg from './checkArg' ; import { SelectOptions , AirtableDeletedResponse , AirtableRecord , AirtableRecordResponse , AirtableUpdateRecord , DeleteResponse , Fields , Config , queryBody , Typecast , updateOpts , bulkQueryBody , } from './types' ; import { request } from './http' ; ignore declare global { interface Window { AsyncAirtable : typeof AsyncAirtable ; } } const validOptions = [ 'fields' , 'filterByFormula' , 'maxRecords' , 'pageSize' , 'sort' , 'view' , 'where' , ] ; /**
 * The main AsyncAirtable class.
 */ export class AsyncAirtable { default retryOnRateLimit : boolean ; default maxRetry : number ; default retryTimeout : number ; /** The API Key from AirTable */ apiKey : string ; /** The base id from AirTable */ base : string ; /** the base URL to use when making API requests */ baseURL : string ; param apiKey param base param config constructor ( apiKey , base , config ? ) { if ( ! apiKey ) throw new Error ( 'API Key is required.' ) ; if ( ! base ) throw new Error ( 'Base ID is required.' ) ; this . apiKey = apiKey ; this . base = base ; this . retryOnRateLimit = config ?. retryOnRateLimit || true ; this . retryTimeout = config ?. retryTimeout || 5000 ; this . maxRetry = config ?. maxRetry || 60000 ; this . baseURL = config ?. baseURL || 'https://api.airtable.com/v0' ; } param table param options param page returns async select = async ( table , options ? , page ? ) : Promise < AirtableRecord [ ] > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( options , 'options' , 'object' , false ) ; checkArg ( page , 'page' , 'number' , false ) ; const url = `${ this . baseURL }/${ this . base }/${ table }` ; const opts = options ? { ... options } : { } ; Object . keys ( opts ) . forEach ( ( option ) => { if ( ! validOptions . includes ( option ) ) { throw new Error ( `Invalid option: ${ option }` ) ; } } ) ; let offset = '' ; let data = [ ] ; if ( page ) { for ( let i = 0 ; i < page ; i ++ ) { if ( offset ) { opts . offset = offset ; } try { const body = await request < AirtableRecordResponse > ( { endpoint : `${ url }?${ buildOpts ( opts ) }` , options : { headers : { Authorization : `Bearer ${ this . apiKey }` } } , instance : this , pageHandler : { index : i , page : page , } , } ) ; if ( i + 1 === page ) { return body . records ; } offset = body . offset ; } catch ( err ) { throw new Error ( err ) ; } } } else { let done = false ; while ( ! done ) { if ( offset ) { opts . offset = offset ; } try { const body = await request ( { endpoint : `${ url }?${ buildOpts ( opts ) }` , options : { headers : { Authorization : `Bearer ${ this . apiKey }` } , } , instance : this , } ) ; data = data . concat ( body . records ) ; offset = body . offset ; if ( ! body . offset ) { done = true ; } } catch ( err ) { throw new Error ( err ) ; } } } return data ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param id returns async find = async ( table , id ) : Promise < AirtableRecord > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( id , 'id' , 'string' ) ; const url = `${ this . baseURL }/${ this . base }/${ table }/${ id }` ; const data = await request ( { endpoint : url , options : { headers : { Authorization : `Bearer ${ this . apiKey }` } , } , instance : this , } ) ; return data ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param record param typecast returns async createRecord = async ( table , record , typecast ? ) : Promise < AirtableRecord > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( record , 'record' , 'object' ) ; checkArg ( typecast , 'typecast' , 'boolean' , false ) ; const url = `${ this . baseURL }/${ this . base }/${ table }` ; const body = { fields : record } ; if ( typecast !== undefined ) { body . typecast = typecast ; } const data = await request ( { endpoint : url , instance : this , options : { method : 'post' , body : JSON . stringify ( body ) , headers : { Authorization : `Bearer ${ this . apiKey }` , 'Content-Type' : 'application/json' , } , } , } ) ; return data ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param record param opts returns async updateRecord = async ( table , record , opts ? ) : Promise < AirtableRecord > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( record , 'record' , 'object' ) ; if ( opts ) { checkArg ( opts . destructive , 'opts.desctructive' , 'boolean' ) ; checkArg ( opts . typecast , 'opts.typecast' , 'boolean' , false ) ; } const url = `${ this . baseURL }/${ this . base }/${ table }/${ record . id }` ; const body = { fields : record . fields } ; if ( opts ?. typecast !== undefined ) { body . typecast = opts ?. typecast ; } const data = await request ( { endpoint : url , instance : this , options : { method : opts ?. destructive ? 'put' : 'patch' , body : JSON . stringify ( body ) , headers : { Authorization : `Bearer ${ this . apiKey }` , 'Content-Type' : 'application/json' , } , } , } ) ; return data ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param id returns async deleteRecord = async ( table , id ) : Promise < DeleteResponse > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( id , 'id' , 'string' ) ; const url = `${ this . baseURL }/${ this . base }/${ table }/${ id }` ; const data = await request ( { endpoint : url , instance : this , options : { method : 'delete' , headers : { Authorization : `Bearer ${ this . apiKey }` , } , } , } ) ; return data ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param records param typecast returns async bulkCreate = async ( table , records , typecast ? ) : Promise < AirtableRecord [ ] > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( records , 'records' , 'array' ) ; checkArg ( typecast , 'typecast' , 'boolean' , false ) ; const url = `${ this . baseURL }/${ this . base }/${ table }` ; const body = { records : records . map ( ( record ) => ( { fields : record , } ) ) , } ; if ( typecast !== undefined ) { body . typecast = typecast ; } const data = await request ( { endpoint : url , options : { method : 'post' , body : JSON . stringify ( body ) , headers : { Authorization : `Bearer ${ this . apiKey }` , 'Content-Type' : 'application/json' , } , } , instance : this , } ) ; return data . records ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param records param opts returns async bulkUpdate = async ( table , records , opts ? ) : Promise < AirtableRecord [ ] > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( records , 'records' , 'array' ) ; if ( opts ) { checkArg ( opts . destructive , 'opts.desctructive' , 'boolean' , false ) ; checkArg ( opts . typecast , 'opts.typecast' , 'boolean' , false ) ; } const url = `${ this . baseURL }/${ this . base }/${ table }` ; const body = { records } ; if ( opts ?. typecast !== undefined ) { body . typecast = opts ?. typecast ; } const data = await request ( { endpoint : url , options : { method : opts ?. destructive ? 'put' : 'patch' , body : JSON . stringify ( body ) , headers : { Authorization : `Bearer ${ this . apiKey }` , 'Content-Type' : 'application/json' , } , } , instance : this , } ) ; return data . records ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param ids returns async bulkDelete = async ( table , ids ) : Promise < DeleteResponse [ ] > => { try { checkArg ( table , 'table' , 'string' ) ; checkArg ( ids , 'ids' , 'array' ) ; let query = '' ; ids . forEach ( ( id , i ) => { if ( i !== 0 ) { query = `${ query }&records[]=${ id }` ; } else { query = `records[]=${ id }` ; } } ) ; const url = `${ this . baseURL }/${ this . base }/${ table }?${ encodeURI ( query ) }` ; const data = await request ( { endpoint : url , options : { method : 'delete' , headers : { Authorization : `Bearer ${ this . apiKey }` , } , } , instance : this , } ) ; return data . records ; } catch ( err ) { throw new Error ( err ) ; } } ; param table param filterString param record param opts returns async upsertRecord = async ( table , filterString , record , opts ? ) : Promise < AirtableRecord > => { checkArg ( table , 'table' , 'string' ) ; checkArg ( filterString , 'filterString' , 'string' ) ; checkArg ( record , 'record' , 'object' ) ; if ( opts ) { checkArg ( opts . destructive , 'opts.desctructive' , 'boolean' , false ) ; checkArg ( opts . typecast , 'opts.typecast' , 'boolean' , false ) ; } const exists = await this . select ( table , { filterByFormula : filterString } ) ; if ( ! exists [ 0 ] ) { return await this . createRecord ( table , record , opts ?. typecast ) ; } else { return await this . updateRecord ( table , { id : exists [ 0 ] . id , fields : record , } , opts ) ; } } ; } if ( typeof window !== 'undefined' ) { window . AsyncAirtable = AsyncAirtable ; } export const CREATED_TIME = ( ) : string => 'CREATED_TIME()' ; export const NOW = ( ) : string => 'NOW()' ; export const TODAY = ( ) : string => 'TODAY()' ; export const ERROR = ( ) : string => 'ERROR()' ; export const LAST_MODIFIED_TIME = ( ) : string => 'LAST_MODIFIED_TIME()' ; export const RECORD_ID = ( ) : string => 'RECORD_ID()' ;