Skip to content

Commit

Permalink
Merge pull request #854 from Thenlie/develop
Browse files Browse the repository at this point in the history
v2.0.2
  • Loading branch information
Thenlie authored Feb 11, 2024
2 parents 212d4af + 035a622 commit a837a8c
Show file tree
Hide file tree
Showing 22 changed files with 40,602 additions and 5,394 deletions.
5 changes: 4 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L93) | 93 | #162 Use MUI ThemeProvider |
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L155) | 155 | Fix deprecated prop |
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L241) | 241 | Add a transition when search is expanded or collapsed |
| [src/screens/DiscoverScreen.tsx](src/screens/DiscoverScreen.tsx#L61) | 61 | #613 Dynamic date range |
| [src/screens/PageNotFoundScreen.tsx](src/screens/PageNotFoundScreen.tsx#L42) | 42 | Implement better error handling |
| [src/screens/PageNotFoundScreen.tsx](src/screens/PageNotFoundScreen.tsx#L43) | 43 | Handle thrown responses with 'isRouteErrorResponse' |
| [src/supabase/profiles.ts](src/supabase/profiles.ts#L253) | 253 | #587 Ensure country code is valid |
| [src/__tests__/screens/DiscoverScreen.test.tsx](src/__tests__/screens/DiscoverScreen.test.tsx#L29) | 29 | #851 Create global sections variable |
| [src/screens/auth/LoginScreen.tsx](src/screens/auth/LoginScreen.tsx#L86) | 86 | We could try to get the AuthApiError type and use 'cause' instead |
| [src/screens/dashboard/DashboardGalleryScreen.tsx](src/screens/dashboard/DashboardGalleryScreen.tsx#L38) | 38 | If profile does not return after a few seconds, |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L38) | 38 | paginate data #838 |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L88) | 88 | Create loader #839 |
| [src/screens/discover/discoverRequests.ts](src/screens/discover/discoverRequests.ts#L54) | 54 | #613 Dynamic date range |
45 changes: 45 additions & 0 deletions cli/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env node

import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { Props, main } from '.';

const yargsInstance = yargs(hideBin(process.argv));

yargsInstance
.options({
o: {
alias: 'outputFile',
default: 'stdio',
describe: 'Filename to write response to',
type: 'string',
},
i: {
alias: 'inputFile',
default: 'data/tmdb_openapi.json',
describe: 'Filename to read open api spec from',
type: 'string',
},
p: {
alias: 'inputPath',
describe: 'TMDB endpoint to be requested',
type: 'string',
},
d: {
alias: 'useDefaults',
describe: 'If the request should use default values and bypass param entry',
type: 'boolean',
},
})
.command(
'run',
'run the CLI tool',
() => {},
(argv) => {
const { outputFile, inputFile, inputPath, useDefaults } = argv as unknown as Props;
main({ outputFile, inputFile, inputPath, useDefaults });
}
)
.wrap(yargsInstance.terminalWidth())
.demandCommand(1)
.parse();
33,352 changes: 33,352 additions & 0 deletions cli/data/tmdb_openapi.json

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions cli/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import fs, { WriteFileOptions } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

interface MakeRequestProps {
path: string;
params: Array<{
param: string;
value: string;
path: boolean;
}>;
outputFile: string | undefined;
}

/**
* Helper function to make fetch request to TMDB and handle output
*/
const makeRequest = async ({ path, params, outputFile }: MakeRequestProps) => {
// Make fetch request and print output
const data = JSON.stringify(await fetchTMDB(path, params), null, 4);
if (outputFile && outputFile !== 'stdio') {
try {
const writeOptions: WriteFileOptions = {
encoding: 'utf8',
// the value 0o666 sets the file to be readable and writable by everyone but not executable
mode: 0o666,
flag: 'w',
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fs.writeFileSync(`${__dirname}/${outputFile}`, data, writeOptions);
// eslint-disable-next-line no-console
console.log('File written successfully!');
} catch (err) {
// eslint-disable-next-line no-console
console.error('Failed to write file!', err);
}
} else {
// eslint-disable-next-line no-console
console.log(data);
}
};

/**
* Make a GET request to The Movie DB API with a given endpoint
* @todo make this method generic and move BASE_PATH and API_KEY to a config file
* @param {string} path
* @param {Array<{ param: string, value: string, path: boolean }>} params
* @returns {Promise<object>}
*/
const fetchTMDB = async (
path: string,
params: Array<{ param: string; value: string; path: boolean }>
): Promise<object> => {
const BASE_PATH = 'https://api.themoviedb.org';
// eslint-disable-next-line no-undef
const API_KEY = '?api_key=' + process.env.VITE_MOVIEDB_KEY;
let PARAMS = '';
if (params.length > 0) {
for (let i = 0; i < params.length; i++) {
if (!params[i].path) {
PARAMS += `&${params[i].param}=${params[i].value}`;
} else {
path = path.replace(`{${params[i].param}}`, params[i].value);
}
}
}
const url = new URL(BASE_PATH + path + API_KEY + PARAMS);
const res = await fetch(url);
if (!res.ok) {
throw new Error(`Failed to fetch: ${String(url)}. Status: ${res.statusText}`);
}
const json = await res.json();
return json;
};

export { makeRequest, fetchTMDB };
94 changes: 94 additions & 0 deletions cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'dotenv/config';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { filterPathsByReqType, getPathParams, validatePath } from './utils.js';
import searchSelect from './searchSelect.js';
import { checkbox, input } from '@inquirer/prompts';
import { makeRequest } from './fetch.js';
import { DEFAULT_PARAMS } from './lib/params.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export interface Props {
outputFile: string | undefined;
inputFile: string;
inputPath: string | undefined;
useDefaults: boolean;
}

export const main = async ({ outputFile, inputFile, inputPath, useDefaults }: Props) => {
// Parse The Movie DB's Open API schema
const json = JSON.parse(fs.readFileSync(`${__dirname}/${inputFile}`, 'utf8'));

// Create path choices
const getReqPaths = filterPathsByReqType(Object.entries(json.paths), 'get');

// Get user selected path
let selectedPath: string;
if (inputPath) {
const isValid = validatePath(inputPath, getReqPaths.map((path) => path[0]) as string[]);
if (!isValid) throw new Error(`Invalid path ${inputPath}!`);
selectedPath = inputPath;
} else {
const pathChoices = getReqPaths.map((path: object) => {
return {
name: path[0],
value: path[0],
description: path[1]['get'].description,
};
});
selectedPath = await searchSelect({
message: 'Select a Movie DB API request',
choices: pathChoices,
});
}

// Create list of user selected OR default query parameters
const selectedParams: Array<{
param: string;
value: string;
path: boolean;
}> = [];
const pathParams = getPathParams(selectedPath);
if (!useDefaults) {
// Get list of all params for selected path
const params = json.paths[selectedPath].get.parameters.map((param) => {
const req = param.required ? ' (required)' : '';
return {
name: param.name + req,
value: param.name,
checked: !!req,
};
});

// Get user selected params
const selectedParamList: string[] = await checkbox({
message: 'Select params to add',
choices: params,
loop: true,
});

// Prompt user for each selected param
for (let i = 0; i < selectedParamList.length; i++) {
const answer = await input({ message: selectedParamList[i] });
const isInPath = pathParams.includes(selectedParamList[i]);
selectedParams.push({ param: selectedParamList[i], value: answer, path: isInPath });
}
} else {
// Get default path params
for (let i = 0; i < pathParams.length; i++) {
const defaultParam = DEFAULT_PARAMS.find((param) => param.name === pathParams[i]);
if (defaultParam) {
selectedParams.push({
param: pathParams[i],
value: defaultParam.values[Math.round(Math.random())],
path: true,
});
}
}
}

await makeRequest({ path: selectedPath, params: selectedParams, outputFile });
};
31 changes: 31 additions & 0 deletions cli/lib/params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* A list of all TMDB path parameters with default values.
*
* @todo Add non-path params
*
* Given a list of paths, you can filter out unique params using the command:
* `grep -Eo '\{\w+\}' <filename> | sort | uniq`
*/
const DEFAULT_PARAMS = [
{ name: 'account_id', values: ['', ''] },
{ name: 'collection_id', values: ['', ''] },
{ name: 'company_id', values: ['3166', '521'] },
{ name: 'credit_id', values: ['52fe4311c3a36847f8037ee9', '52fe4311c3a36847f8037ec7'] },
{ name: 'episode_id', values: ['385571', '62085'] },
{ name: 'episode_number', values: ['1', '2'] },
{ name: 'external_id', values: ['', ''] },
{ name: 'guest_session_id', values: ['', ''] },
{ name: 'keyword_id', values: ['', ''] },
{ name: 'list_id', values: ['', ''] },
{ name: 'movie_id', values: ['1726', '4232'] },
{ name: 'network_id', values: ['174', '6'] },
{ name: 'person_id', values: ['17419', '70851'] },
{ name: 'review_id', values: ['59cc634fc3a3682aa30065a3', '6313ce428c7b0f0082be0687'] },
{ name: 'season_id', values: ['7240', '3572'] },
{ name: 'season_number', values: ['1', '2'] },
{ name: 'series_id', values: ['2316', '1396'] },
{ name: 'time_window', values: ['day', 'week'] },
{ name: 'tv_episode_group_id', values: ['', ''] },
];

export { DEFAULT_PARAMS };
Loading

0 comments on commit a837a8c

Please sign in to comment.