Skip to content
This repository has been archived by the owner on Dec 8, 2023. It is now read-only.

Commit

Permalink
Merge pull request #173 from newrelic/campfire/nr-1674-gh-api-files
Browse files Browse the repository at this point in the history
Fetch files with Github API
  • Loading branch information
josephgregoryii authored May 16, 2022
2 parents 3993ff3 + 3dc6165 commit 962b6a8
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,11 @@ export const LOCALS = [
},
];


/* Quickstart Preview */

export const GITHUB_API_BASE_URL =
'https://api.github.com/repos/newrelic/newrelic-quickstarts/contents';

export const GITHUB_API_PULL_URL =
'https://api.github.com/repos/newrelic/newrelic-quickstarts/pulls';
70 changes: 70 additions & 0 deletions src/pages/preview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useEffect, useState } from 'react';

import PropTypes from 'prop-types';
import { getQuickstartFilesFromPR } from '../utils/preview/fetchHelpers';
import { navigate } from 'gatsby';

const PreviewPage = ({ location }) => {
const [contentFiles, setContentFiles] = useState([]);

// TODO: Make this into a custom hook to reduce useEffect usage
useEffect(() => {
// grab query parameters to determine if it is a local preview or
// PR preview
const urlParams = new URLSearchParams(location.search);
const prNumber = urlParams.get('pr');
const quickstartPath = urlParams.get('quickstart');

// check to make sure query parameters are set
// otherwise, return home
if (!prNumber || !quickstartPath) {
console.log('Error: Missing query parameters');
if (!prNumber) {
console.log('prNumber');
}
if (!quickstartPath) {
console.log('quickstartPath');
}

navigate('/');
return;
}

/*
* Async function to walk the file system in Github
* and set the content to a stateful variable.
**/
const fetchRawFiles = async () => {
try {
const fileContent = await getQuickstartFilesFromPR(
prNumber,
quickstartPath
);
setContentFiles(fileContent);
} catch (error) {
console.log('Error:', error.message);
navigate('/');
return;
}
};
fetchRawFiles();
}, []);

// To console log the results as part of AC
// TODO: Remove/refactor this in parsing implementation
useEffect(() => {
if (!contentFiles) {
return;
}

console.log(contentFiles);
}, [contentFiles]);

return <span>oh hai</span>;
};

PreviewPage.propTypes = {
location: PropTypes.object.isRequired,
};

export default PreviewPage;
115 changes: 115 additions & 0 deletions src/utils/preview/fetchHelpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { GITHUB_API_BASE_URL, GITHUB_API_PULL_URL } from '../../data/constants';

/**
* Recursive function to walk the file system in Github
* @param {String} url - Github API URL to walk the file system
* @returns {Array} array of files
**/
export const iterateDirs = async (url) => {
const response = await fetch(url);
if (response.status !== 200 || !response.ok) {
throw new Error(
`Response came back while walking the file tree with status ${response.status}\nFetched URL: ${url}`
);
}
const json = await response.json();
let fileAggregator = [];

for (const dirListing of json) {
if (dirListing.type === 'dir') {
const dirFiles = await iterateDirs(dirListing.url);
fileAggregator = [...fileAggregator, ...dirFiles];
} else {
fileAggregator = [...fileAggregator, dirListing];
}
}
return fileAggregator;
};

/**
* Function handles retreiving the type of file for parsing
* @param {String} fileName - file name to check
* @returns {String} - type of file
**/
export const getFileType = (fileName) => {
// Regex for different filetypes
const imageFileTypes = /^.*\.(jpg|jpeg|svg|png)$/i;
const yamlFileTypes = /^.*\.(yaml|yml)$/i;
const jsonFileType = /^.*\.(json)$/i;

if (fileName.match(imageFileTypes)) {
return 'image';
} else if (fileName.match(yamlFileTypes)) {
return 'yaml';
} else if (fileName.match(jsonFileType)) {
return 'json';
}
};

/**
* Function determines the type of content depending on type of image
* @param {Object} - Metadata object to parse
* @param {Object}.download_url - raw file URL
* @param {Object}.name - name of the file
* @returns {Object} - Object of file containing type, fileName, and content of
* file
**/
export const determineContent = async ({
download_url,
name: fileName,
path,
}) => {
const type = getFileType(fileName);
let rawFileResponse = null;

if (type !== 'image') {
rawFileResponse = await fetch(download_url);
}

// if the file is an image, we can supply the URL to the raw content
const content =
type === 'image' ? download_url : await rawFileResponse.text();

const filePath = path.split('quickstarts/').pop();

return {
type,
filePath,
fileName,
content,
};
};

/**
* Function grabs the raw content from files
* @param {Array} fileAggregator - array of Github metadata objects
* @returns {Promise<Array<Object>>} - array of objects containg raw content to parse
*
**/
export const getRawContent = (fileAggregator) => {
return Promise.all(fileAggregator.map(determineContent));
};

/**
* Async function handles fetching changed files in pull request from Github
**/
export const getQuickstartFilesFromPR = async (prNumber, quickstartPath) => {
// Hit the Github API for SHA that references the PR branch
const response = await fetch(`${GITHUB_API_PULL_URL}/${prNumber}`);

if (response.status !== 200 || !response.ok) {
throw new Error(
`Response from pull request came back with status ${response.status}\n`
);
}

// Response containing files at root of PR
const json = await response.json();
const branchSHA = json.head.sha;

// Recursively walk the Github API from the root of the quickstart
const fileAggregator = await iterateDirs(
`${GITHUB_API_BASE_URL}/quickstarts/${quickstartPath}?ref=${branchSHA}`
);
return getRawContent(fileAggregator);
};

0 comments on commit 962b6a8

Please sign in to comment.