How do I get type target?.history from GitHub GraphQL on defaultBranchRef

95 Views Asked by At

I'm trying to get the last commit with typesaftey but can't get past the history property because it can be an empty object, {}, null, undefined, or the actual object I want, Commit.

My GitHub GraphQL query is

query OrgReposAgExtended_v3(
  $organization: String!
  $pageSize: Int
  $after: String
) {
  organization(login: $organization) {
    repositories(
      first: $pageSize
      after: $after
      orderBy: { field: STARGAZERS, direction: DESC }
    ) {
      totalCount
      pageInfo {
        startCursor
        hasNextPage
        endCursor
      }
      edges {
        cursor
        node {
          ...RepoEx
        }
      }
    }
  }
}
fragment Commit on Commit {
  message
  pushedDate
  committedDate
}
fragment RepoEx on Repository {
  repositoryName: name
  id
  url
  lastPushToDefaultBranch: defaultBranchRef {
    name
    target {
      ... on Commit {
        history(first: 1) {
          edges {
            node {
              # this is the object I want
              ...Commit
            }
          }
        }
      }
    }
  }
}

The response from a codegen'd SDK is

{
  organization: {
    repositories: {
      totalCount: 2130,
      pageInfo: {
        startCursor: 'Y3Vyc29yOnYyOpLNBc3OB9Ef4Q==',
        hasNextPage: true,
        endCursor: 'Y3Vyc29yOnYyOpLNBc3OB9Ef4Q=='
      },
      edges: [
        {
          cursor: 'Y3Vyc29yOnYyOpLNBc3OB9Ef4Q==',
          node: {
            repositoryName: 'cognitive-services-speech-sdk',
            id: 'MDEwOlJlcG9zaXRvcnkxMzExNDU2OTc=',
            url: 'https://github.com/Azure-Samples/cognitive-services-speech-sdk',
            lastPushToDefaultBranch: {
              name: 'master',
              target: {
                history: {
                  edges: [
                    {
                      node: {
                        message:
                          'pull 1.25 new samples and updates to public GitHub repository. (#1812)\n\n* pull 1.25 new samples and updates to public GitHub repository.\r\n\r\n* also update the sdk version used by all the samples.\r\n\r\n* add step to install maui-android, so new maui smaples will build in ci.\r\n\r\n* adding the maui-android workflow did not fix the restore fialure.  Exclude the maui project from CiCd build like in carbon.\r\n\r\n* adding the maui-android workflow did not fix the restore fialure.  Exclude the maui project from CiCd build like in carbon.\r\n\r\n* adding the maui-android workflow did not fix the restore fialure.  Exclude the maui project from CiCd build like in carbon.\r\n\r\n* adding the maui-android workflow did not fix the restore fialure.  Exclude the maui project from CiCd build like in carbon.\r\n\r\n* adding the maui-android workflow did not fix the restore fialure.  Exclude the maui project from CiCd build like in carbon.',
                        pushedDate: '2023-01-28T04:29:05Z',
                        committedDate: '2023-01-28T04:29:02Z'
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      ]
    }
  }
}

It is typed from codegen as (formatted to read)

export type IRepoExFragment = { 
    id: string, 
    url: any, 
    repositoryName: string, 
    lastPushToDefaultBranch?: { 
        name: string, 
        target?: { 
            history: { 
                edges?: Array<{ 
                    node?: { 
                        message: string, 
                        pushedDate?: any | null | undefined, 
                        committedDate: any } | null | undefined 
                } | null | undefined> | null | undefined 
            } 
        } | {} | null | undefined // how to do I by pass this to get the history object?
    } | null | undefined 
};

Source code function in repo

export async function reposExtended({
  pat,
  gitHubGraphQLUrl,
  orgName
}: IRepoParameters2): Promise<IRepoExRefactored | null> {
  if (!pat) {
    throw new Error('GitHub Personal Access Token is required')
  }
  if (!gitHubGraphQLUrl) {
    throw new Error('GitHub GraphQL URL is required')
  }
  if (!orgName) {
    throw new Error('orgName is required')
  }

  const page_size: InputMaybe<number> = 1 // Max page size for GitHub
  const variables: IOrgReposAgExtended_V3QueryVariables = {
    organization: orgName,
    pageSize: page_size,
    after: null
  }
  const data = await reposExQueryGraphQlSDK(gitHubGraphQLUrl, pat, variables)
  const repo =
    data.organization?.repositories?.edges &&
    data.organization?.repositories?.edges.length > 0
      ? data.organization?.repositories?.edges[0]?.node
      : null

  if (repo === null) return null
  if (JSON.stringify(repo) === JSON.stringify({})) return null

  let repoReturned = null

  if (repo !== null && repo !== undefined) {
    repoReturned = {
      id: repo.id || '',
      url: repo.url || '',
      lastPushToDefaultBranch: null
    }

    // Get last commit
    const lastCommitTarget = repo.lastPushToDefaultBranch?.target

    if (lastCommitTarget !== null && lastCommitTarget !== undefined) {
      const history = lastCommitTarget.history

      // just to see what history is
      console.log(history)
    }
  }

  return repoReturned
}

Build error is

src/sdk/v3/repos_extended.ts:70:40 - error TS2339: Property 'history' does not exist on type '{} | { history: { edges?: ({ node?: { message: string; pushedDate?: any; committedDate: any; } | null | undefined; } | null | undefined)[] | null | undefined; }; }'.
  Property 'history' does not exist on type '{}'.

70       const history = lastCommitTarget.history
                                          ~~~~~~~

src/sdk/v3/repos_extended.ts:77:3 - error TS2322: Type '{ id: string; url: any; lastPushToDefaultBranch: null; } | null' is not assignable to type 'IRepoExRefactored | null'.
  Type '{ id: string; url: any; lastPushToDefaultBranch: null; }' is not assignable to type 'IRepoExRefactored'.
    Types of property 'lastPushToDefaultBranch' are incompatible.
      Type 'null' is not assignable to type '{ name: string; message: string | null | undefined; pushedDate?: string | null | undefined; committedDate: string | null | undefined; status?: string | undefined; } | undefined'.

77   return repoReturned

Codegen'd sdk is here in repo - I know I shouldn't check in gen'd SDK - this is just for reference to help understand the underlying types.

My repo is here

1

There are 1 best solutions below

0
DFBerry On

With help from @Xirzec, I have the correct code. You can clean it up to suit your needs.

The simple explanation is:

declare var x: {} | {'a':string, 'b': number } | null | undefined

// combination of null/undefined check and property `in` object
if(x !==null && x !== undefined && "a" in x ){
    // x is not empty or null or undefined
    console.log(x.a)
}

This specific scenario is:

// THIS IS THE FIX TO GET PAST THROUGH HISTORY
const lastCommitTarget = repo.lastPushToDefaultBranch?.target
let commit

if (
  lastCommitTarget !== null &&
  lastCommitTarget !== undefined &&
  'history' in lastCommitTarget
) {
  const history = lastCommitTarget.history

  if (
    history?.edges !== null &&
    history?.edges !== undefined &&
    history?.edges.length > 0
  ) {
    const node = history?.edges[0]?.node

    // THIS IS THE COMMIT
    console.log(node);
  }
}