import long from "long" ; import { ReportOptions } from "./types" ; import { services , fields } from "./protos" ; import { normaliseQuery } from "./utils" ; export const ParsingError = { NO_REPORT_OPTIONS_OR_GAQL_QUERY : "Must provided reportOptions or gaqlString to parse results." , NO_FIELDS_IN_GAQL_QUERY : "GAQL Query must contain at least one attribute, metric or segment." , NO_FIELDS_IN_REPORT_OPTIONS : "Report Options must contain at least one attribute, metric or segment." , } ; description example export function parse ( { results , reportOptions , gaqlString , } ) { if ( results . length === 0 ) { return results ; } if ( typeof reportOptions === "undefined" && typeof gaqlString === "undefined" ) { throw new Error ( ParsingError . NO_REPORT_OPTIONS_OR_GAQL_QUERY ) ; } const queryFields = reportOptions ? getReportOptionFields ( reportOptions ) : getGAQLFields ( gaqlString ! ) ; const allFields = [ ... queryFields , ... fields . resourceNames ] ; return parseRows ( results , allFields ) ; } export function getGAQLFields ( gaqlString ) { const normalisedQuery = normaliseQuery ( gaqlString ) ; const fields = normalisedQuery . toLowerCase ( ) . replace ( /(^\s*select)|( from .*)|(\s+)/g , "" ) . split ( "," ) . filter ( ( field ) => field . length > 0 ) ; if ( ! fields . length ) { throw new Error ( ParsingError . NO_FIELDS_IN_GAQL_QUERY ) ; } return fields ; } export function getReportOptionFields ( reportOptions ) { const fields = [ ... ( reportOptions . attributes || [ ] ) , ... ( reportOptions . metrics || [ ] ) , ... ( reportOptions . segments || [ ] ) , ] ; if ( ! fields . length ) { throw new Error ( ParsingError . NO_FIELDS_IN_REPORT_OPTIONS ) ; } return fields ; } export function parseRows ( rows , fields ) { const newRows = [ ] ; for ( let r = 0 ; r < rows . length ; r ++ ) { const newRow = { } ; const originalRow = services . GoogleAdsRow . fromObject ( rows [ r ] ) ; for ( let f = 0 ; f < fields . length ; f ++ ) { const field = fields [ f ] ; const split = field . split ( "." ) ; const [ parent , ... children ] = split ; if ( ! originalRow [ parent ] ) { continue ; } ( newRow as any ) [ parent ] = parseNestedValues ( ( newRow as any ) [ parent ] , originalRow [ parent ] , parent , children ) ; } newRows . push ( newRow ) ; } return newRows ; } function parseNestedValues ( row , data , field , paths ) { if ( ! data ) return null ; const [ parentField , ... childFields ] = paths ; if ( ! row ) row = { } ; if ( childFields . length === 0 ) { const rawVal = data [ parentField ] ; const parsedVal = long . isLong ( rawVal ) ? new long ( rawVal . low , rawVal . high , rawVal . unsigned ) . toNumber ( ) : rawVal ; row [ parentField as fields . Resource ] = parsedVal ; return row ; } ( row as any ) [ parentField ] = parseNestedValues ( ( row as any ) [ parentField ] , data [ parentField ] , parentField , childFields ) ; return row ; }