Skip to content

Commit

Permalink
#12: Search button does nothing
Browse files Browse the repository at this point in the history
#13: WARNING: The FTGO_BACKEND_API_URL variable is not set. Defaulting to a blank string
#15: Backend server does not run: - sh: babel: not found
 - changed CORS parameters for the server and the client
  • Loading branch information
dartandrevinsky committed May 28, 2021
1 parent 59610f0 commit 44a53d2
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 22 deletions.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.circleci
build
ci-artefacts
node_modules
reports
server
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ RUN npx envinfo > ./envinfo.log
RUN cat ./envinfo.log

ARG REACT_APP_BACKEND_API_URL
#ENV REACT_APP_BACKEND_API_URL $REACT_APP_BACKEND_API_URL
ENV REACT_APP_BACKEND_API_URL $REACT_APP_BACKEND_API_URL
RUN echo "REACT_APP_BACKEND_API_URL: $REACT_APP_BACKEND_API_URL"

COPY package.json .
COPY package-lock.json .
RUN npm config set unsafe-perm true && npm install -g serve
ENV NODE_OPTIONS --max_old_space_size=1024
ENV NODE_OPTIONS --max_old_space_size=819
RUN npm ci
#RUN npm ci --ignore-scripts
ADD src ./src
Expand Down
5 changes: 2 additions & 3 deletions build-and-test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ echo "20. Installing dependencies"

echo "running npm install in $(pwd)"
rm -rf node_modules
rm -f package-lock.json
npm install --no-audit # --loglevel verbose
npm audit fix
#rm -f package-lock.json
npm ci # --loglevel verbose


echo ""
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build:slim": "GENERATE_SOURCEMAP=false react-scripts build",
"build:slim": "GENERATE_SOURCEMAP=false react-scripts build --expose-gc",
"test": "$(npm bin)/jest --env=jest-environment-jsdom-sixteen --config=./src/jest.config.js --watchAll=false --bail --runInBand --reporters=default --reporters=jest-junit --collectCoverage=true --coverageDirectory=$JEST_COVERAGE_OUTPUT_DIR --unhandled-rejections=strict --verbose",
"test:dev": "source ./ensure-variables-and-paths.sh && $(npm bin)/jest --env=jest-environment-jsdom-sixteen --config=./src/jest.config.js --watchAll=true --reporters=default --reporters=jest-junit --unhandled-rejections=strict --verbose",
"serve": "serve -s build -C -l 5000 --debug",
Expand Down
2 changes: 2 additions & 0 deletions server/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
49 changes: 38 additions & 11 deletions src/features/actions/api.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import { safelyExecuteAsync } from '../../shared/promises';
import { ensureEnvVariable } from '../../shared/env';

const API_URL = (`${process.env.REACT_APP_BACKEND_API_URL}/api`).replace(/\/\/api$/i, '/api');
const API_URL = ensureEnvVariable('REACT_APP_BACKEND_API_URL', window.location.origin);

const urlResolver = obtainFQDNUrl(API_URL, window.location);

const apiRoutes = prepareRoutesForFetch({
postAddressObtainRestaurants: [
`POST ${ API_URL }/address`,
`POST /address`,
(address, time, origin) => ({ address, time, origin })
],
getRestaurantById: restaurantId => `${ API_URL }/restaurants/${ restaurantId }`,
postCreateNewCart: `POST ${ API_URL }/cart`,
getRestaurantById: restaurantId => `/restaurants/${ restaurantId }`,
postCreateNewCart: `POST /cart`,
putUpdateCartWithItem: [
(cartId, restaurantId, itemId, qty) => `PUT ${ API_URL }/cart/${ cartId }`,
(cartId, restaurantId, itemId, qty) => `PUT /cart/${ cartId }`,
(cartId, restaurantId, itemId, qty) => ({ cartId, restaurantId, itemId, qty })
]
});
}, urlResolver);

export const { postAddressObtainRestaurants, getRestaurantById, putUpdateCartWithItem, postCreateNewCart } = apiRoutes;

function prepareRoutesForFetch(routes) {
function prepareRoutesForFetch(routes, urlResolver) {
return Object.fromEntries(Array.from(Object.entries(routes), ([ k, v ]) => {
const [ resource, init ] = Array.isArray(v) ? v : [ v ];
return [ k, async (...args) => {
const [ method, url ] = parseResource(resource, args);
const [ method, url ] = parseResource(resource, args, urlResolver);
const [ fetchErr, response ] = await safelyExecuteAsync(fetch(typeof url === 'function' ? url(...args) : url, {
method,
...(init ? { body: JSON.stringify(init(...args)) } : {}),
mode: 'no-cors', // no-cors, *cors, same-origin
mode: 'cors', // no-cors, *cors, same-origin
headers: {
'Content-Type': 'application/json'
},
Expand All @@ -41,7 +44,31 @@ function prepareRoutesForFetch(routes) {
}));
}

function parseResource(input, args) {
function parseResource(input, args, urlResolver) {
const parts = (((typeof input === 'function') ? input(...args) : input).split(/\s+/));
return (parts.length === 1) ? [ 'GET', parts[ 0 ] ] : [ parts[ 0 ].toUpperCase(), parts[ 1 ] ];
return (parts.length === 1) ? [ 'GET', urlResolver(parts[ 0 ]) ] : [ parts[ 0 ].toUpperCase(), urlResolver(parts[ 1 ]) ];
}

function obtainFQDNUrl(baseUrl, location) {
const resolvedLocation = resolveBaseUrl(baseUrl, location);
resolvedLocation.pathname = 'api';
const resolvedAPILocation = resolvedLocation.toString();

return pathPart =>
[
resolvedAPILocation,
pathPart
].join('/').replace(`${ resolvedAPILocation }//`, `${ resolvedAPILocation }/`);
}

function resolveBaseUrl(baseUrl, location) {
try {
const result = new window.URL(baseUrl);
if (result.origin == null) {
return new URL(`${ location.protocol }${ baseUrl }`);
}
return result;
} catch (ex) {
return new URL(`${ location.protocol }${ baseUrl }`);
}
}
4 changes: 2 additions & 2 deletions src/features/actions/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { push } from 'connected-react-router';
import { routePaths } from '../../ui/pages/AppRoutes/routePaths';
import { debugged } from '../../shared/diagnostics';

export const navigateToPickRestaurants = () => push(routePaths.restaurants);
export const navigateToEditDeliveryAddress = () => push(routePaths.landing);
export const navigateToEditMenu = (restaurantId) => /anon/.test(restaurantId) ? debugged(restaurantId): push(routePaths.restaurant.replace(':placeId', restaurantId));
export const navigateToPickRestaurants = () => push(routePaths.restaurants);
export const navigateToEditMenu = (restaurantId) => push(routePaths.restaurant.replace(':placeId', restaurantId));
export const navigateToCheckout = () => push(routePaths.checkout);
61 changes: 61 additions & 0 deletions src/shared/env/env.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { getEnvVar } from './envGetter';
import { ensureEnvVariable, ensureEnvVariables } from './index';

jest.mock('./envGetter');

const ref = {
current: null
};

getEnvVar.mockImplementation((...args) => ref.current && ref.current(...args))

describe('A set of utilities to check presence of env variables', () => {

beforeAll(() => {
ref.current = key => ({
'DEFINED': 'true',
'DEFINED2': '2'
})[ key ];
});

describe('ensureEnvVariable()', () => {

test('Checks the existence of an env variable and returns its value', () => {
expect(ensureEnvVariable('DEFINED')).toEqual('true');
expect(ensureEnvVariable('DEFINED2')).toEqual('2');
});

test('Throws if a variable is not defined', () => {
expect(() => ensureEnvVariable('UN-DEFINED')).toThrow();
});

test('Returns the supplied default value (even falsey) if a variable is not defined', () => {
expect(ensureEnvVariable('UN-DEFINED-2', 2)).toEqual(2);
expect(ensureEnvVariable('UN-DEFINED-0', 0)).toEqual(0);
expect(ensureEnvVariable('UN-DEFINED-false', false)).toEqual(false);
expect(ensureEnvVariable('UN-DEFINED-empty', '')).toEqual('');
});

});

describe('ensureEnvVariables()', () => {

test('Can check several env variables at once, returning an array of their values', () => {
expect(ensureEnvVariables([ 'DEFINED', 'DEFINED2' ])).toEqual([ 'true', '2' ]);
});

test('Throws if any of the requested variables are not defined, error message contains missing variable names', () => {
expect(() => ensureEnvVariables([ 'UN-DEFINED', 'UN-DEFINED2', 'DEFINED' ])).toThrow(/(UN-DEFINED.+UN-DEFINED2)|(UN-DEFINED2.+UN-DEFINED)/);
});

test('Returns the supplied default value for the corresponding index', () => {
expect(ensureEnvVariables(
[ 'DEFINED', 'UN-DEFINED-2', 'UN-DEFINED-0', 'UN-DEFINED-false', 'UN-DEFINED-empty'
],
[ undefined, 2, 0, false, '' ]
)).toEqual([ 'true', 2, 0, false, '' ]);
});

});

});
2 changes: 2 additions & 0 deletions src/shared/env/envGetter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

export const getEnvVar = envVar => process.env[ envVar ];
29 changes: 29 additions & 0 deletions src/shared/env/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { getEnvVar } from './envGetter';

/**
* @description this will be replaced by ensureEnvVariablesWithParameterStore()
* @param envVars
* @param fallbackValues
* @returns {String[]}
*/
export function ensureEnvVariables(envVars, fallbackValues) {
const hitsAndMisses = Array.from(envVars).map((envVar, idx) => ({
env: envVar,
val: getEnvVar(envVar),
fallback: fallbackValues && fallbackValues[ idx ]
}));

const misses = hitsAndMisses.filter(({ val, fallback }) => !val && (typeof fallback === 'undefined')).map(({ env }) => env);

if (misses.length) {
throw new Error(`Set up these environment variables: ${ misses.join(', ') }`);
}

return hitsAndMisses.map(({ val, fallback }) => (val || fallback));
}


export function ensureEnvVariable(envVar, fallback) {
const [ result ] = ensureEnvVariables([ envVar ], [ fallback ]);
return result;
}
11 changes: 9 additions & 2 deletions src/ui/pages/CheckoutPage/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { accessCart, accessCartItems, accessCartStatus } from '../../../features/cart/cartSlice';
import { navigateToEditMenu } from '../../../features/actions/navigation';
import { navigateToEditMenu, navigateToPickRestaurants } from '../../../features/actions/navigation';
import { Container } from 'reactstrap';
import { accessSelectedRestaurantId } from '../../../features/restaurants/restaurantsSlice';

const CheckoutPage = () => {
const dispatch = useDispatch();
const cartStatus = useSelector(accessCartStatus());
const cartItems = useSelector(accessCartItems());
const cartId = useSelector(accessCart('id'));
const selectedRestaurantId = useSelector(accessSelectedRestaurantId());

void cartItems;

useEffect(() => {
if (!cartId || (cartStatus !== 'ready')) {
return null;
}
dispatch(navigateToEditMenu());
if (selectedRestaurantId) {
dispatch(navigateToEditMenu(selectedRestaurantId));
} else {
dispatch(navigateToPickRestaurants());
}
}, [ cartId, cartStatus, dispatch ]);

if (!cartId || (cartStatus !== 'ready')) {
Expand Down
2 changes: 1 addition & 1 deletion src/ui/pages/RestaurantPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const RestaurantPage = ({ match }) => {
}, [ dispatch ]);

useEffect(() => {
if (selectedRestaurantId && urlRestaurantId) {
if (selectedRestaurantId && urlRestaurantId && (selectedRestaurantId === urlRestaurantId)) {
return;
}
dispatch(resetSelectedRestaurant());
Expand Down

0 comments on commit 44a53d2

Please sign in to comment.