From 51e7944a12fd4b027e321358455b2d29cd860188 Mon Sep 17 00:00:00 2001 From: Christopher Carroll Smith Date: Sat, 9 Dec 2023 19:15:53 -0500 Subject: [PATCH] Re-bundle the package --- __tests__/index.test.js | 36 +++++----- dist/index.js | 149 +++++++++++++++++++++++++++++----------- src/index.js | 66 +++++++++++------- 3 files changed, 168 insertions(+), 83 deletions(-) diff --git a/__tests__/index.test.js b/__tests__/index.test.js index d9bc6aa..e4c438a 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -7,12 +7,12 @@ import { extract } from '@extractus/feed-extractor' jest.mock('@extractus/feed-extractor', () => ({ extract: jest.fn() -})); +})) // Mock the core module jest.mock('@actions/core', () => ({ setFailed: jest.fn() -})); +})) // Mock the fs module except for readFileSync jest.mock('fs', () => ({ @@ -22,7 +22,7 @@ jest.mock('fs', () => ({ existsSync: jest.fn(), mkdirSync: jest.fn(), writeFileSync: jest.fn() -})); +})) beforeEach(() => { // Reset the mocks and environment variables @@ -32,9 +32,9 @@ beforeEach(() => { process.env.INPUT_PARSER_OPTIONS = JSON.stringify({ useISODateFormat: true }) process.env.INPUT_FETCH_OPTIONS = '{}' process.env.INPUT_REMOVE_PUBLISHED = 'false' -}); +}) -describe('fetchRssFeed function', () => { +describe('fetchRssFeed function', () => { describe('Validation tests', () => { it('should set failed if feed URL is not provided', async () => { // Clear the environment variable for feed URL @@ -47,7 +47,7 @@ describe('fetchRssFeed function', () => { expect(core.setFailed).toHaveBeenCalledWith( 'After parsing, feedURL is not an array of non-empty strings' ) - }); + }) it('should set failed if feed URL is not a valid URL', async () => { // Set an invalid URL for feed URL @@ -60,7 +60,7 @@ describe('fetchRssFeed function', () => { expect(core.setFailed).toHaveBeenCalledWith( 'Invalid URL provided: invalid URL' ) - }); + }) it('should set failed if file path is not provided', async () => { // Clear the environment variable for file path @@ -70,8 +70,10 @@ describe('fetchRssFeed function', () => { await fetchRssFeed() // Expect that core.setFailed was called with the expected error message - expect(core.setFailed).toHaveBeenCalledWith('After parsing, filePath is not an array of non-empty strings') - }); + expect(core.setFailed).toHaveBeenCalledWith( + 'After parsing, filePath is not an array of non-empty strings' + ) + }) it('should set failed if file extension is not .json', async () => { // Set the environment variable for file path with an invalid extension @@ -84,8 +86,8 @@ describe('fetchRssFeed function', () => { expect(core.setFailed).toHaveBeenCalledWith( 'File path is invalid: File path extension must be .json' ) - }); - }); + }) + }) describe('Input Parsing', () => { it('should set failed if parserOptions is invalid JSON', async () => { @@ -201,7 +203,7 @@ describe('fetchRssFeed function', () => { './feed1.json', './feed2.json' ]) - + // Verify that extract function is mocked expect(jest.isMockFunction(extract)).toBe(true) @@ -212,11 +214,11 @@ describe('fetchRssFeed function', () => { await fetchRssFeed() // Verify that extract was called twice with the correct arguments - expect(extract).toHaveBeenCalledTimes(2); + expect(extract).toHaveBeenCalledTimes(2) expect(extract).toHaveBeenCalledWith( - 'https://example.com/feed1', - { useISODateFormat: true }, - {} + 'https://example.com/feed1', + { useISODateFormat: true }, + {} ) expect(extract).toHaveBeenCalledWith( 'https://example.com/feed2', @@ -352,7 +354,7 @@ describe('fetchRssFeed function', () => { it('should set failed if directory cannot be created', async () => { // Return javascript object from extract function extract.mockResolvedValueOnce({ title: 'Test Feed' }) - + // Setup fs.existsSync.mockReturnValue(true) fs.writeFileSync.mockImplementationOnce(() => { diff --git a/dist/index.js b/dist/index.js index 97ec491..8382c1b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2733,13 +2733,30 @@ async function fetchRssFeed() { try { const feedExtractor = await __nccwpck_require__.e(/* import() */ 290).then(__nccwpck_require__.bind(__nccwpck_require__, 2290)) const extract = feedExtractor.extract - const feedUrl = process.env.INPUT_FEED_URL - const filePath = process.env.INPUT_FILE_PATH + const removePublished = process.env.INPUT_REMOVE_PUBLISHED - let parserOptions - let fetchOptions + let filePaths + try { + // Try to parse the file path as JSON. This will work if it's an array or a "stringified" single path + const jsonFilePath = JSON.parse(process.env.INPUT_FILE_PATH) + filePaths = Array.isArray(jsonFilePath) ? jsonFilePath : [jsonFilePath] + } catch (jsonError) { + // If JSON.parse fails, assume file path is a regular string (single path) and wrap it in an array + filePaths = [process.env.INPUT_FILE_PATH] + } + let feedUrls + try { + // Try to parse the feed URL as JSON. This will work if it's an array or a "stringified" single URL + const jsonFeedUrl = JSON.parse(process.env.INPUT_FEED_URL) + feedUrls = Array.isArray(jsonFeedUrl) ? jsonFeedUrl : [jsonFeedUrl] + } catch (jsonError) { + // If JSON.parse fails, assume feed is a regular string (single URL) and wrap it in an array + feedUrls = [process.env.INPUT_FEED_URL] + } + + let parserOptions try { parserOptions = JSON.parse(process.env.INPUT_PARSER_OPTIONS || '{}') } catch (jsonError) { @@ -2748,6 +2765,7 @@ async function fetchRssFeed() { ) } + let fetchOptions try { fetchOptions = JSON.parse(process.env.INPUT_FETCH_OPTIONS || '{}') } catch (jsonError) { @@ -2756,19 +2774,45 @@ async function fetchRssFeed() { ) } - // If parserOptions contains a function as a string, eval it (Be cautious with eval) + // If parserOptions contains a function as a string, eval it + try { + if ( + parserOptions.getExtraEntryFields && + typeof parserOptions.getExtraEntryFields === 'string' + ) { + parserOptions.getExtraEntryFields = eval( + `(${parserOptions.getExtraEntryFields})` + ) + } + } catch (error) { + throw new Error( + `Failed to evaluate getExtraEntryFields function: ${error.message}` + ) + } + + // Validate feedUrls if ( - parserOptions.getExtraEntryFields && - typeof parserOptions.getExtraEntryFields === 'string' + !feedUrls.length || + feedUrls.length === 0 || + !feedUrls.every(url => typeof url === 'string' && url.length > 0) ) { - parserOptions.getExtraEntryFields = eval( - `(${parserOptions.getExtraEntryFields})` + throw new Error( + 'After parsing, feedURL is not an array of non-empty strings' + ) + } + + if (feedUrls.length !== filePaths.length) { + throw new Error( + 'After parsing, feedURL and filePath arrays do not have the same length' ) } - // Validate feedUrl - if (!feedUrl || typeof feedUrl !== 'string') { - throw new Error('Feed URL is not provided or invalid') + for (const url of feedUrls) { + try { + new URL(url) // This will throw an error if url is not a valid URL + } catch { + throw new Error(`Invalid URL provided: ${url}`) + } } // Validate and convert removePublished to boolean, if provided @@ -2782,43 +2826,66 @@ async function fetchRssFeed() { } // Validate filePath - if (!filePath) { - throw new Error('File path is not provided') - } else if (!['.json'].includes(path.extname(filePath).toLowerCase())) { - throw new Error('File path extension must be .json') + if ( + !filePaths.length || + filePaths.length === 0 || + !filePaths.every(file => typeof file === 'string' && file.length > 0) + ) { + throw new Error( + 'After parsing, filePath is not an array of non-empty strings' + ) } - - // Fetch and parse the feed - let parsedData try { - parsedData = await extract(feedUrl, parserOptions, fetchOptions) - } catch (extractError) { - throw new Error(`Failed to fetch or parse feed: ${extractError.message}`) - } + for (const filePath of filePaths) { + if (!['.json'].includes(path.extname(filePath).toLowerCase())) { + throw new Error('File path extension must be .json') + } + } + } catch (error) { + throw new Error(`File path is invalid: ${error.message}`) + } + + for (let i = 0; i < feedUrls.length; i++) { + const feedUrl = feedUrls[i] + const filePath = filePaths[i] + + // Fetch and parse the feed + let parsedData + try { + parsedData = await extract(feedUrl, parserOptions, fetchOptions) + } catch (extractError) { + throw new Error( + `Failed to fetch or parse feed: ${extractError.message}` + ) + } - // Validate parsedData - if (!parsedData || typeof parsedData !== 'object') { - throw new Error('Parsed data is invalid') - } + // Validate parsedData + if (!parsedData || typeof parsedData !== 'object') { + throw new Error(`Parsed data is invalid for feed ${feedUrl}`) + } - // Remove top-level published field if removePublished is set to true - if (removePublishedBool) { - delete parsedData.published - } + // Remove top-level published field if removePublished is set to true + if (removePublishedBool) { + delete parsedData?.published + } - // Check if directory exists; if not, create it - const dir = path.dirname(filePath) - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir, { recursive: true }) - } + try { + // Check if directory exists; if not, create it + const dir = path.dirname(filePath) + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }) + } - // Write the parsed data to the file - const jsonFeedData = JSON.stringify(parsedData, null, 2) - fs.writeFileSync(filePath, jsonFeedData) + // Write the parsed data to the file + const jsonFeedData = JSON.stringify(parsedData, null, 2) + fs.writeFileSync(filePath, jsonFeedData) - console.log(`RSS feed saved to ${filePath} successfully!`) + console.log(`RSS feed saved to ${filePath} successfully!`) + } catch (error) { + throw new Error(`Failed to write to file: ${error.message}`) + } + } } catch (error) { - console.error('Error fetching RSS feed:', error) core.setFailed(error.message) } } diff --git a/src/index.js b/src/index.js index 64eae87..e2715cf 100644 --- a/src/index.js +++ b/src/index.js @@ -6,27 +6,27 @@ async function fetchRssFeed() { try { const feedExtractor = await import('@extractus/feed-extractor') const extract = feedExtractor.extract - + const removePublished = process.env.INPUT_REMOVE_PUBLISHED - let filePaths; + let filePaths try { // Try to parse the file path as JSON. This will work if it's an array or a "stringified" single path - const jsonFilePath = JSON.parse(process.env.INPUT_FILE_PATH); - filePaths = Array.isArray(jsonFilePath) ? jsonFilePath : [jsonFilePath]; + const jsonFilePath = JSON.parse(process.env.INPUT_FILE_PATH) + filePaths = Array.isArray(jsonFilePath) ? jsonFilePath : [jsonFilePath] } catch (jsonError) { // If JSON.parse fails, assume file path is a regular string (single path) and wrap it in an array - filePaths = [process.env.INPUT_FILE_PATH]; + filePaths = [process.env.INPUT_FILE_PATH] } - let feedUrls; + let feedUrls try { // Try to parse the feed URL as JSON. This will work if it's an array or a "stringified" single URL - const jsonFeedUrl = JSON.parse(process.env.INPUT_FEED_URL); - feedUrls = Array.isArray(jsonFeedUrl) ? jsonFeedUrl : [jsonFeedUrl]; + const jsonFeedUrl = JSON.parse(process.env.INPUT_FEED_URL) + feedUrls = Array.isArray(jsonFeedUrl) ? jsonFeedUrl : [jsonFeedUrl] } catch (jsonError) { // If JSON.parse fails, assume feed is a regular string (single URL) and wrap it in an array - feedUrls = [process.env.INPUT_FEED_URL]; + feedUrls = [process.env.INPUT_FEED_URL] } let parserOptions @@ -64,21 +64,29 @@ async function fetchRssFeed() { } // Validate feedUrls - if (!feedUrls.length || feedUrls.length === 0 || !feedUrls.every(url => typeof url === 'string' && url.length > 0)) { - throw new Error('After parsing, feedURL is not an array of non-empty strings'); + if ( + !feedUrls.length || + feedUrls.length === 0 || + !feedUrls.every(url => typeof url === 'string' && url.length > 0) + ) { + throw new Error( + 'After parsing, feedURL is not an array of non-empty strings' + ) } if (feedUrls.length !== filePaths.length) { - throw new Error('After parsing, feedURL and filePath arrays do not have the same length'); - } + throw new Error( + 'After parsing, feedURL and filePath arrays do not have the same length' + ) + } - feedUrls.forEach(url => { + for (const url of feedUrls) { try { - new URL(url); // This will throw an error if url is not a valid URL + new URL(url) // This will throw an error if url is not a valid URL } catch { - throw new Error(`Invalid URL provided: ${url}`); + throw new Error(`Invalid URL provided: ${url}`) } - }); + } // Validate and convert removePublished to boolean, if provided let removePublishedBool = false // Default value @@ -91,29 +99,37 @@ async function fetchRssFeed() { } // Validate filePath - if (!filePaths.length || filePaths.length === 0 || !filePaths.every(file => typeof file === 'string' && file.length > 0)) { - throw new Error('After parsing, filePath is not an array of non-empty strings'); + if ( + !filePaths.length || + filePaths.length === 0 || + !filePaths.every(file => typeof file === 'string' && file.length > 0) + ) { + throw new Error( + 'After parsing, filePath is not an array of non-empty strings' + ) } try { - filePaths.forEach(filePath => { + for (const filePath of filePaths) { if (!['.json'].includes(path.extname(filePath).toLowerCase())) { throw new Error('File path extension must be .json') } - }); + } } catch (error) { throw new Error(`File path is invalid: ${error.message}`) } for (let i = 0; i < feedUrls.length; i++) { - const feedUrl = feedUrls[i]; - const filePath = filePaths[i]; - + const feedUrl = feedUrls[i] + const filePath = filePaths[i] + // Fetch and parse the feed let parsedData try { parsedData = await extract(feedUrl, parserOptions, fetchOptions) } catch (extractError) { - throw new Error(`Failed to fetch or parse feed: ${extractError.message}`) + throw new Error( + `Failed to fetch or parse feed: ${extractError.message}` + ) } // Validate parsedData