Skip to content

Commit a837a8c

Browse files
authored
Merge pull request #854 from Thenlie/develop
v2.0.2
2 parents 212d4af + 035a622 commit a837a8c

22 files changed

+40602
-5394
lines changed

TODO.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L93) | 93 | #162 Use MUI ThemeProvider |
77
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L155) | 155 | Fix deprecated prop |
88
| [src/components/Navigation.tsx](src/components/Navigation.tsx#L241) | 241 | Add a transition when search is expanded or collapsed |
9-
| [src/screens/DiscoverScreen.tsx](src/screens/DiscoverScreen.tsx#L61) | 61 | #613 Dynamic date range |
109
| [src/screens/PageNotFoundScreen.tsx](src/screens/PageNotFoundScreen.tsx#L42) | 42 | Implement better error handling |
1110
| [src/screens/PageNotFoundScreen.tsx](src/screens/PageNotFoundScreen.tsx#L43) | 43 | Handle thrown responses with 'isRouteErrorResponse' |
1211
| [src/supabase/profiles.ts](src/supabase/profiles.ts#L253) | 253 | #587 Ensure country code is valid |
12+
| [src/__tests__/screens/DiscoverScreen.test.tsx](src/__tests__/screens/DiscoverScreen.test.tsx#L29) | 29 | #851 Create global sections variable |
1313
| [src/screens/auth/LoginScreen.tsx](src/screens/auth/LoginScreen.tsx#L86) | 86 | We could try to get the AuthApiError type and use 'cause' instead |
1414
| [src/screens/dashboard/DashboardGalleryScreen.tsx](src/screens/dashboard/DashboardGalleryScreen.tsx#L38) | 38 | If profile does not return after a few seconds, |
15+
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L38) | 38 | paginate data #838 |
16+
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L88) | 88 | Create loader #839 |
17+
| [src/screens/discover/discoverRequests.ts](src/screens/discover/discoverRequests.ts#L54) | 54 | #613 Dynamic date range |

cli/command.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env node
2+
3+
import yargs from 'yargs';
4+
import { hideBin } from 'yargs/helpers';
5+
import { Props, main } from '.';
6+
7+
const yargsInstance = yargs(hideBin(process.argv));
8+
9+
yargsInstance
10+
.options({
11+
o: {
12+
alias: 'outputFile',
13+
default: 'stdio',
14+
describe: 'Filename to write response to',
15+
type: 'string',
16+
},
17+
i: {
18+
alias: 'inputFile',
19+
default: 'data/tmdb_openapi.json',
20+
describe: 'Filename to read open api spec from',
21+
type: 'string',
22+
},
23+
p: {
24+
alias: 'inputPath',
25+
describe: 'TMDB endpoint to be requested',
26+
type: 'string',
27+
},
28+
d: {
29+
alias: 'useDefaults',
30+
describe: 'If the request should use default values and bypass param entry',
31+
type: 'boolean',
32+
},
33+
})
34+
.command(
35+
'run',
36+
'run the CLI tool',
37+
() => {},
38+
(argv) => {
39+
const { outputFile, inputFile, inputPath, useDefaults } = argv as unknown as Props;
40+
main({ outputFile, inputFile, inputPath, useDefaults });
41+
}
42+
)
43+
.wrap(yargsInstance.terminalWidth())
44+
.demandCommand(1)
45+
.parse();

cli/data/tmdb_openapi.json

Lines changed: 33352 additions & 0 deletions
Large diffs are not rendered by default.

cli/fetch.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import fs, { WriteFileOptions } from 'fs';
2+
import path from 'path';
3+
import { fileURLToPath } from 'url';
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = path.dirname(__filename);
7+
8+
interface MakeRequestProps {
9+
path: string;
10+
params: Array<{
11+
param: string;
12+
value: string;
13+
path: boolean;
14+
}>;
15+
outputFile: string | undefined;
16+
}
17+
18+
/**
19+
* Helper function to make fetch request to TMDB and handle output
20+
*/
21+
const makeRequest = async ({ path, params, outputFile }: MakeRequestProps) => {
22+
// Make fetch request and print output
23+
const data = JSON.stringify(await fetchTMDB(path, params), null, 4);
24+
if (outputFile && outputFile !== 'stdio') {
25+
try {
26+
const writeOptions: WriteFileOptions = {
27+
encoding: 'utf8',
28+
// the value 0o666 sets the file to be readable and writable by everyone but not executable
29+
mode: 0o666,
30+
flag: 'w',
31+
};
32+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
33+
fs.writeFileSync(`${__dirname}/${outputFile}`, data, writeOptions);
34+
// eslint-disable-next-line no-console
35+
console.log('File written successfully!');
36+
} catch (err) {
37+
// eslint-disable-next-line no-console
38+
console.error('Failed to write file!', err);
39+
}
40+
} else {
41+
// eslint-disable-next-line no-console
42+
console.log(data);
43+
}
44+
};
45+
46+
/**
47+
* Make a GET request to The Movie DB API with a given endpoint
48+
* @todo make this method generic and move BASE_PATH and API_KEY to a config file
49+
* @param {string} path
50+
* @param {Array<{ param: string, value: string, path: boolean }>} params
51+
* @returns {Promise<object>}
52+
*/
53+
const fetchTMDB = async (
54+
path: string,
55+
params: Array<{ param: string; value: string; path: boolean }>
56+
): Promise<object> => {
57+
const BASE_PATH = 'https://api.themoviedb.org';
58+
// eslint-disable-next-line no-undef
59+
const API_KEY = '?api_key=' + process.env.VITE_MOVIEDB_KEY;
60+
let PARAMS = '';
61+
if (params.length > 0) {
62+
for (let i = 0; i < params.length; i++) {
63+
if (!params[i].path) {
64+
PARAMS += `&${params[i].param}=${params[i].value}`;
65+
} else {
66+
path = path.replace(`{${params[i].param}}`, params[i].value);
67+
}
68+
}
69+
}
70+
const url = new URL(BASE_PATH + path + API_KEY + PARAMS);
71+
const res = await fetch(url);
72+
if (!res.ok) {
73+
throw new Error(`Failed to fetch: ${String(url)}. Status: ${res.statusText}`);
74+
}
75+
const json = await res.json();
76+
return json;
77+
};
78+
79+
export { makeRequest, fetchTMDB };

cli/index.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import 'dotenv/config';
2+
import fs from 'fs';
3+
import path from 'path';
4+
import { fileURLToPath } from 'url';
5+
import { filterPathsByReqType, getPathParams, validatePath } from './utils.js';
6+
import searchSelect from './searchSelect.js';
7+
import { checkbox, input } from '@inquirer/prompts';
8+
import { makeRequest } from './fetch.js';
9+
import { DEFAULT_PARAMS } from './lib/params.js';
10+
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
13+
14+
export interface Props {
15+
outputFile: string | undefined;
16+
inputFile: string;
17+
inputPath: string | undefined;
18+
useDefaults: boolean;
19+
}
20+
21+
export const main = async ({ outputFile, inputFile, inputPath, useDefaults }: Props) => {
22+
// Parse The Movie DB's Open API schema
23+
const json = JSON.parse(fs.readFileSync(`${__dirname}/${inputFile}`, 'utf8'));
24+
25+
// Create path choices
26+
const getReqPaths = filterPathsByReqType(Object.entries(json.paths), 'get');
27+
28+
// Get user selected path
29+
let selectedPath: string;
30+
if (inputPath) {
31+
const isValid = validatePath(inputPath, getReqPaths.map((path) => path[0]) as string[]);
32+
if (!isValid) throw new Error(`Invalid path ${inputPath}!`);
33+
selectedPath = inputPath;
34+
} else {
35+
const pathChoices = getReqPaths.map((path: object) => {
36+
return {
37+
name: path[0],
38+
value: path[0],
39+
description: path[1]['get'].description,
40+
};
41+
});
42+
selectedPath = await searchSelect({
43+
message: 'Select a Movie DB API request',
44+
choices: pathChoices,
45+
});
46+
}
47+
48+
// Create list of user selected OR default query parameters
49+
const selectedParams: Array<{
50+
param: string;
51+
value: string;
52+
path: boolean;
53+
}> = [];
54+
const pathParams = getPathParams(selectedPath);
55+
if (!useDefaults) {
56+
// Get list of all params for selected path
57+
const params = json.paths[selectedPath].get.parameters.map((param) => {
58+
const req = param.required ? ' (required)' : '';
59+
return {
60+
name: param.name + req,
61+
value: param.name,
62+
checked: !!req,
63+
};
64+
});
65+
66+
// Get user selected params
67+
const selectedParamList: string[] = await checkbox({
68+
message: 'Select params to add',
69+
choices: params,
70+
loop: true,
71+
});
72+
73+
// Prompt user for each selected param
74+
for (let i = 0; i < selectedParamList.length; i++) {
75+
const answer = await input({ message: selectedParamList[i] });
76+
const isInPath = pathParams.includes(selectedParamList[i]);
77+
selectedParams.push({ param: selectedParamList[i], value: answer, path: isInPath });
78+
}
79+
} else {
80+
// Get default path params
81+
for (let i = 0; i < pathParams.length; i++) {
82+
const defaultParam = DEFAULT_PARAMS.find((param) => param.name === pathParams[i]);
83+
if (defaultParam) {
84+
selectedParams.push({
85+
param: pathParams[i],
86+
value: defaultParam.values[Math.round(Math.random())],
87+
path: true,
88+
});
89+
}
90+
}
91+
}
92+
93+
await makeRequest({ path: selectedPath, params: selectedParams, outputFile });
94+
};

cli/lib/params.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* A list of all TMDB path parameters with default values.
3+
*
4+
* @todo Add non-path params
5+
*
6+
* Given a list of paths, you can filter out unique params using the command:
7+
* `grep -Eo '\{\w+\}' <filename> | sort | uniq`
8+
*/
9+
const DEFAULT_PARAMS = [
10+
{ name: 'account_id', values: ['', ''] },
11+
{ name: 'collection_id', values: ['', ''] },
12+
{ name: 'company_id', values: ['3166', '521'] },
13+
{ name: 'credit_id', values: ['52fe4311c3a36847f8037ee9', '52fe4311c3a36847f8037ec7'] },
14+
{ name: 'episode_id', values: ['385571', '62085'] },
15+
{ name: 'episode_number', values: ['1', '2'] },
16+
{ name: 'external_id', values: ['', ''] },
17+
{ name: 'guest_session_id', values: ['', ''] },
18+
{ name: 'keyword_id', values: ['', ''] },
19+
{ name: 'list_id', values: ['', ''] },
20+
{ name: 'movie_id', values: ['1726', '4232'] },
21+
{ name: 'network_id', values: ['174', '6'] },
22+
{ name: 'person_id', values: ['17419', '70851'] },
23+
{ name: 'review_id', values: ['59cc634fc3a3682aa30065a3', '6313ce428c7b0f0082be0687'] },
24+
{ name: 'season_id', values: ['7240', '3572'] },
25+
{ name: 'season_number', values: ['1', '2'] },
26+
{ name: 'series_id', values: ['2316', '1396'] },
27+
{ name: 'time_window', values: ['day', 'week'] },
28+
{ name: 'tv_episode_group_id', values: ['', ''] },
29+
];
30+
31+
export { DEFAULT_PARAMS };

0 commit comments

Comments
 (0)