0

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

DFBerry
  • 1,818
  • 1
  • 19
  • 37

1 Answers1

0

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);
  }
}  
DFBerry
  • 1,818
  • 1
  • 19
  • 37