1
\$\begingroup\$

I was learning some DOM manipulation and interaction with third-party APIs by building a simple interface to get the people who commented on a PR on GitHub.

I got great feedback for my initial prototype: Interface for getting Github PRs commenters using Github API.

Now I am trying to get into OOP using ES6 Classes, so I build this GitHubPR class to handle all the interaction with the GitHub API:

const apiUrl = 'https://api.github.com'

export default class GitHubPR {
  constructor(prUrl) {
    GitHubPR.validatePRUrl(prUrl)
    this.prUrl = new URL(prUrl)
    this.prApiUrl = GitHubPR.prUrlToApiEndpoint(prUrl)
  }

  static validatePRUrl(prUrl) {
    if (!GitHubPR.isValidPRUrl(prUrl)) {
      throw new Error('Not a valid GitHub Pull Request Url.')
    }
    return prUrl
  }

  static isValidPRUrl(prUrl) {
    const githubPullRequestPattern = RegExp(
      /^https:\/\/github.com\/[\w-_.+]+\/[\w-_.+]+\/pull\/\d+\/?$/
    )
    return githubPullRequestPattern.test(prUrl)
  }

  static prUrlToApiEndpoint(prUrl) {
    GitHubPR.validatePRUrl(prUrl)
    const { pathname } = new URL(prUrl)
    const prApiUrl = new URL(
      `/repos${pathname.replace('/pull/', '/pulls/')}`,
      apiUrl
    )
    return prApiUrl
  }

  static async fetch(endpoint) {
    const apiOptions = {
      headers: {
        Accept: 'application/vnd.github.v3+json',
      },
    }
    const url = endpoint.startsWith(apiUrl) ? endpoint : `${apiUrl}${endpoint}`
    return (await fetch(url, apiOptions)).json()
  }

  fetchInfo() {
    return GitHubPR.fetch(this.prApiUrl.pathname)
  }

  fetchReviews() {
    return GitHubPR.fetch(`${this.prApiUrl.pathname}/reviews`)
  }

  async fetchReviewers() {
    const reviews = await GitHubPR.fetch(`${this.prApiUrl.pathname}/reviews`)
    const uniqueReviewers = [...new Set(reviews.map(review => review.user.url))]
    return Promise.all(
      uniqueReviewers.map(reviewer => GitHubPR.fetch(reviewer))
    )
  }
}

I would love to have some feedback on this. I am new to OOP and ES6 classes, so not sure if this is following good practices or if I am making good use for a class here, or how could I make this class more robust according to OOP principles.

My idea is being able to use it in something like this:

import GitHubPR from './github-pr'

document
  .querySelector('#pull-request-form')
  .addEventListener('submit', onsubmit)

async function onsubmit(event) {
  event.preventDefault()
  const pr = new GitHubPR(document.querySelector('#pull-request-url').value)
  const [info, reviewers] = await Promise.all([
    pr.fetchInfo(),
    pr.fetchReviewers(),
  ])
  renderInfo(info)
  renderReviewers(reviewers) 
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.