import fs from "fs" ; import { FILES } from "./path" ; import { GoogleAdsApi , services , resources , enums } from "../src" ; import { capitaliseFirstLetter , toCamelCase } from "../src/utils" ; interface Resource { attributes : string [ ] ; metrics : string [ ] ; segments : string [ ] ; } const CLIENT_ID = process . env . GADS_API_CLIENT_ID as string ; const CLIENT_SECRET = process . env . GADS_API_CLIENT_SECRET as string ; const DEVELOPER_TOKEN = process . env . GADS_API_DEVELOPER_TOKEN as string ; const REFRESH_TOKEN = process . env . GADS_API_REFRESH_TOKEN as string ; const CUSTOMER_ID = process . env . GADS_API_CUSTOMER_ID as string ; const LOGIN_CUSTOMER_ID = process . env . GADS_API_LOGIN_CUSTOMER_ID as string ; const client = new GoogleAdsApi ( { client_id : CLIENT_ID , client_secret : CLIENT_SECRET , developer_token : DEVELOPER_TOKEN , } ) ; export async function compileFields ( ) { const cus = client . Customer ( { refresh_token : REFRESH_TOKEN , customer_id : CUSTOMER_ID , login_customer_id : LOGIN_CUSTOMER_ID , } ) ; const [ fields ] = await cus . googleAdsFields . searchGoogleAdsFields ( new services . SearchGoogleAdsFieldsRequest ( { query : `
          SELECT
            name,
            category,
            selectable,
            selectable_with,
            attribute_resources,
            filterable,
            data_type,
            metrics,
            segments,
            type_url
        ` , } ) ) ; const resourceConstructs = { } ; const enumFields = { } ; const resourceNames = [ ] ; const attributes = [ ] ; const segments = [ ] ; const metrics = [ ] ; fields . forEach ( ( field ) => { if ( ! isDefinedAndNotNull ( field . name ) ) { return ; } const field_name = field . name as string ; if ( isResource ( field ) ) { const resource = { attributes : [ ] , metrics : field . metrics , segments : field . segments , } ; const selectableResources = [ field_name , ... field . selectable_with . filter ( ( selectable ) => ! selectable . includes ( "segments." ) && ! selectable . includes ( "metrics." ) ) , ] ; resource . attributes = fields . filter ( ( field ) => { const correctResource = selectableResources . find ( ( resource ) => field . resource_name . includes ( `/${ resource }.` ) ) ; return ( isAttribute ( field ) && isDefinedAndNotNull ( field . name ) && correctResource && field . selectable ) ; } ) . map ( ( field ) : string => field . name as string ) ; resourceConstructs [ field_name ] = resource ; } else if ( isAttribute ( field ) && field . selectable ) { attributes . push ( field_name ) ; if ( isResourceName ( field ) ) { resourceNames . push ( field_name ) ; } } else if ( isMetric ( field ) && field . selectable ) { metrics . push ( field_name ) ; } else if ( isSegment ( field ) && field . selectable ) { segments . push ( field_name ) ; } if ( hasEnumDataType ( field ) ) { enumFields [ field_name ] = getEnumName ( field ) ; } } ) ; const stream = fs . createWriteStream ( FILES . fields ) ; stream . write ( `/* Autogenerated File! Do Not Edit */\n` ) ; stream . write ( `\n// eslint-disable-next-line\n` ) ; stream . write ( `export namespace fields {\n` ) ; stream . write ( `\n/*  -- RESOURCES --  */` ) ; buildUnionType ( stream , Object . keys ( resourceConstructs ) , "Resource" ) ; stream . write ( `\n/*  -- ATTRBIUTES --  */` ) ; buildUnionType ( stream , attributes , "Attribute" ) ; stream . write ( `\n/*  -- METRICS --  */` ) ; buildUnionType ( stream , metrics , "Metric" ) ; stream . write ( `\n/*  -- SEGMENTS --  */` ) ; buildUnionType ( stream , segments , "Segment" ) ; stream . write ( `\n\n/*  -- RESOURCE NAMES --  */` ) ; buildUnionArray ( stream , resourceNames , "resourceNames" ) ; stream . write ( `\n\n/*  -- ENUM FIELDS --  */` ) ; stream . write ( `\nexport const enumFields = ${ JSON . stringify ( enumFields ) }` ) ; Object . entries ( resourceConstructs ) . forEach ( ( entry ) => { const resourceName = capitaliseFirstLetter ( toCamelCase ( entry [ 0 ] ) ) ; const resource = entry [ 1 ] ; stream . write ( `\n\n/* --- Start of ${ resourceName } --- */` ) ; buildUnionType ( stream , resource . attributes , `${ resourceName }Field` ) ; if ( resource . metrics . length ) { buildUnionType ( stream , resource . metrics , `${ resourceName }Metric` ) ; } if ( resource . segments . length ) { buildUnionType ( stream , resource . segments , `${ resourceName }Segment` ) ; } stream . write ( `/* --- End of ${ resourceName } --- */` ) ; } ) ; stream . write ( `}` ) ; stream . end ( ) ; } function buildUnionType ( stream , fields , name ) { stream . write ( `\nexport type ${ name } = \n` ) ; fields . forEach ( ( field ) => { stream . write ( `| "${ field }"\n` ) ; } ) ; stream . write ( `\nexport type ${ name }s = Array<${ name }>\n` ) ; } function buildUnionArray ( stream , fields , name ) { stream . write ( `\nexport const ${ name } = [\n` ) ; fields . forEach ( ( field ) => { stream . write ( `"${ field }",\n` ) ; } ) ; stream . write ( `]\n` ) ; } export function isResource ( field ) { return ( enums . GoogleAdsFieldCategory [ field . category ] === enums . GoogleAdsFieldCategory . RESOURCE ) ; } export function isAttribute ( field ) { return ( enums . GoogleAdsFieldCategory [ field . category ] === enums . GoogleAdsFieldCategory . ATTRIBUTE ) ; } export function isMetric ( field ) { return ( enums . GoogleAdsFieldCategory [ field . category ] === enums . GoogleAdsFieldCategory . METRIC ) ; } export function isSegment ( field ) { return ( enums . GoogleAdsFieldCategory [ field . category ] === enums . GoogleAdsFieldCategory . SEGMENT ) ; } const resourceNameRegex = new RegExp ( /^.*\.resource_name$/g ) ; export function isResourceName ( field ) { if ( isDefinedAndNotNull ( field . name ) ) { return resourceNameRegex . test ( field . name as string ) ; } else { return false ; } } export function hasEnumDataType ( field ) { return field . data_type === "ENUM" ; } export function getEnumName ( field ) { if ( isDefinedAndNotNull ( field . type_url ) ) { return ( field . type_url as string ) . replace ( /.*(?=\.)\./g , "" ) ; } else { return "FIELD_HAS_NO_ENUM_NAME" ; } } function isDefinedAndNotNull ( input ) { return typeof input !== "undefined" && input !== null ; }