diff --git a/src/api-client.js b/src/api-client.js new file mode 100644 index 0000000..0b2e224 --- /dev/null +++ b/src/api-client.js @@ -0,0 +1,87 @@ +const axios = require('axios') +const core = require('@actions/core') + +async function enablePagesSite({ repositoryNwo, githubToken }) { + const pagesEndpoint = `https://api.github.com/repos/${repositoryNwo}/pages` + + try { + const response = await axios.post( + pagesEndpoint, + { build_type: 'workflow' }, + { + headers: { + Accept: 'application/vnd.github.v3+json', + Authorization: `Bearer ${githubToken}`, + 'Content-type': 'application/json' + } + } + ) + + const pageObject = response.data + return pageObject + } catch (error) { + if (error.response && error.response.status === 409) { + return null + } + + throw error + } +} + +async function getPagesSite({ repositoryNwo, githubToken }) { + try { + const pagesEndpoint = `https://api.github.com/repos/${repositoryNwo}/pages` + + const response = await axios.get(pagesEndpoint, { + headers: { + Accept: 'application/vnd.github.v3+json', + Authorization: `Bearer ${githubToken}` + } + }) + + const pageObject = response.data + return pageObject + } catch (error) { + throw error + } +} + +async function findOrCreatePagesSite({ repositoryNwo, githubToken, enablement = true }) { + let pageObject + + // Try to find an existing Pages site first + try { + pageObject = await getPagesSite({ repositoryNwo, githubToken }) + } catch (error) { + if (!enablement) { + core.error('Get Pages site failed', error) + throw error + } + core.warning('Get Pages site failed', error) + } + + if (!pageObject && enablement) { + // Create a new Pages site if one doesn't exist + try { + pageObject = await enablePagesSite({ repositoryNwo, githubToken }) + } catch (error) { + core.error('Create Pages site failed', error) + throw error + } + + // This somehow implies that the Pages site was already created but initially failed to be retrieved. + // Try one more time for this extreme edge case! + if (pageObject == null) { + try { + pageObject = await getPagesSite({ repositoryNwo, githubToken }) + } catch (error) { + core.error('Get Pages site still failed', error) + throw error + } + } + } + + return pageObject +} + +module.exports = { findOrCreatePagesSite, enablePagesSite, getPagesSite } diff --git a/src/api-client.test.js b/src/api-client.test.js new file mode 100644 index 0000000..1565443 --- /dev/null +++ b/src/api-client.test.js @@ -0,0 +1,199 @@ +const core = require('@actions/core') +const axios = require('axios') + +const apiClient = require('./api-client') + +describe('apiClient', () => { + const GITHUB_REPOSITORY = 'paper-spa/is-awesome' + const GITHUB_TOKEN = 'gha-token' + + beforeEach(() => { + jest.restoreAllMocks() + + // Mock error/warning/info/debug + jest.spyOn(core, 'error').mockImplementation(jest.fn()) + jest.spyOn(core, 'warning').mockImplementation(jest.fn()) + jest.spyOn(core, 'info').mockImplementation(jest.fn()) + jest.spyOn(core, 'debug').mockImplementation(jest.fn()) + }) + + + describe('enablePagesSite', () => { + it('makes a request to create a page', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.resolve({ status: 201, data: pageObject })) + + const result = await apiClient.enablePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + expect(result).toEqual(pageObject) + }) + + it('handles a 409 response when the page already exists', async () => { + jest + .spyOn(axios, 'post') + .mockImplementationOnce(() => Promise.reject({ response: { status: 409 } })) + + // Simply assert that no error is raised + const result = await apiClient.enablePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + + expect(result).toBe(null) + }) + + it('re-raises errors on failure status codes', async () => { + jest + .spyOn(axios, 'post') + .mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + + let erred = false + try { + await apiClient.enablePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + } catch (error) { + erred = true + expect(error.response.status).toEqual(404) + } + expect(erred).toBe(true) + }) + }) + + describe('getPagesSite', () => { + it('makes a request to get a page', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.resolve({ status: 200, data: pageObject })) + + const result = await apiClient.getPagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + expect(result).toEqual(pageObject) + }) + + it('re-raises errors on failure status codes', async () => { + jest + .spyOn(axios, 'get') + .mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + + let erred = false + try { + await apiClient.getPagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + } catch (error) { + erred = true + expect(error.response.status).toEqual(404) + } + expect(erred).toBe(true) + }) + }) + + describe('findOrCreatePagesSite', () => { + it('does not make a request to create a page if it already exists', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.resolve({ status: 200, data: pageObject })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + + const result = await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + expect(result).toEqual(pageObject) + expect(axios.get).toHaveBeenCalledTimes(1) + expect(axios.post).toHaveBeenCalledTimes(0) + }) + + it('makes request to create a page by default if it does not exist', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.resolve({ status: 201, data: pageObject })) + + const result = await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + expect(result).toEqual(pageObject) + expect(axios.get).toHaveBeenCalledTimes(1) + expect(axios.post).toHaveBeenCalledTimes(1) + }) + + it('makes a request to create a page when explicitly enabled if it does not exist', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.resolve({ status: 201, data: pageObject })) + + const result = await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN, + enablement: true + }) + expect(result).toEqual(pageObject) + expect(axios.get).toHaveBeenCalledTimes(1) + expect(axios.post).toHaveBeenCalledTimes(1) + }) + + it('does not make a request to create a page when explicitly disabled even if it does not exist', async () => { + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.reject({ response: { status: 500 } })) // just so they both aren't 404 + + let erred = false + try { + await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN, + enablement: false + }) + } catch (error) { + erred = true + // re-raised error + expect(error.response.status).toEqual(404) + } + expect(erred).toBe(true) + expect(axios.get).toHaveBeenCalledTimes(1) + expect(axios.post).toHaveBeenCalledTimes(0) + }) + + it('does not make a second request to get page if create fails for reason other than existence', async () => { + jest.spyOn(axios, 'get').mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.reject({ response: { status: 500 } })) // just so they both aren't 404 + + let erred = false + try { + await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + } catch (error) { + erred = true + // re-raised error + expect(error.response.status).toEqual(500) + } + expect(erred).toBe(true) + expect(axios.get).toHaveBeenCalledTimes(1) + expect(axios.post).toHaveBeenCalledTimes(1) + }) + + it('makes second request to get page if create fails because of existence', async () => { + const pageObject = { html_url: 'https://paper-spa.github.io/is-awesome/' } + jest.spyOn(axios, 'get') + .mockImplementationOnce(() => Promise.reject({ response: { status: 404 } })) + .mockImplementationOnce(() => Promise.resolve({ status: 200, data: pageObject })) + jest.spyOn(axios, 'post').mockImplementationOnce(() => Promise.reject({ response: { status: 409 } })) + + const result = await apiClient.findOrCreatePagesSite({ + repositoryNwo: GITHUB_REPOSITORY, + githubToken: GITHUB_TOKEN + }) + expect(result).toEqual(pageObject) + expect(axios.get).toHaveBeenCalledTimes(2) + expect(axios.post).toHaveBeenCalledTimes(1) + }) + }) + +})