Skip to content

Commit

Permalink
37-parse-environment-variables-before-running-the-application (#38)
Browse files Browse the repository at this point in the history
* Create environment variables parser

* Remove react-native-dotenv and replace by built-in module
  • Loading branch information
mgonzalezg9 authored May 7, 2024
1 parent e20ea54 commit 91bf9f2
Show file tree
Hide file tree
Showing 12 changed files with 62 additions and 53 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ Install the project node and npm version via `nvm` by using `nvm install`.
Then install `yarn` in case you don't have it and add the following `.env` file:

```env
OPENWEATHER_API_KEY=XXXXXXXXX
OPENWEATHER_URL=XXXXXXXXX
UNSPLASH_API_KEY=XXXXXXXXX
UNSPLASH_URL=XXXXXXXXX
EXPO_PUBLIC_OPENWEATHER_API_KEY=XXXXXXXXX
EXPO_PUBLIC_OPENWEATHER_URL=XXXXXXXXX
EXPO_PUBLIC_UNSPLASH_API_KEY=XXXXXXXXX
EXPO_PUBLIC_UNSPLASH_URL=XXXXXXXXX
```

Once done this run `yarn install` to install the dependencies and... That's it!
Expand Down
1 change: 0 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: [["module:react-native-dotenv"]],
};
};
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.73.6",
"react-native-dotenv": "^3.4.9",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-svg": "14.1.0",
Expand Down
8 changes: 8 additions & 0 deletions src/config/dotenv/decs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
declare namespace NodeJS {
interface ProcessEnv {
EXPO_PUBLIC_OPENWEATHER_API_KEY: string;
EXPO_PUBLIC_OPENWEATHER_URL: string;
EXPO_PUBLIC_UNSPLASH_API_KEY: string;
EXPO_PUBLIC_UNSPLASH_URL: string;
}
}
13 changes: 13 additions & 0 deletions src/config/dotenv/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Checks all the environment variables are present and available
* @param vars Environment variables
* @returns If all the required keys have been defined
*/
export const hasEnvironmentVariables = (vars: NodeJS.ProcessEnv) => {
return (
vars.EXPO_PUBLIC_OPENWEATHER_API_KEY &&
vars.EXPO_PUBLIC_OPENWEATHER_URL &&
vars.EXPO_PUBLIC_UNSPLASH_API_KEY &&
vars.EXPO_PUBLIC_UNSPLASH_URL
);
};
7 changes: 0 additions & 7 deletions src/decs.d.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
declare module "@env" {
export const OPENWEATHER_API_KEY: string;
export const OPENWEATHER_URL: string;
export const UNSPLASH_API_KEY: string;
export const UNSPLASH_URL: string;
}

declare module 'country-list-spanish';

// Declares images so we can use them with Typescript
Expand Down
6 changes: 6 additions & 0 deletions src/navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* https://reactnavigation.org/docs/getting-started
*
*/
import { hasEnvironmentVariables } from "@/config/dotenv";
import LocationDetailsScreen from "@/screens/LocationDetails";
import LocationRequestScreen from "@/screens/LocationRequest";
import NotFoundScreen from "@/screens/NotFoundScreen";
Expand All @@ -20,6 +21,11 @@ LogBox.ignoreLogs([
"Non-serializable values were found in the navigation state",
]);

// Parse environment variables
if (!hasEnvironmentVariables(process.env)) {
throw new Error('Please provide all the required environment variables')
}

export default function Navigation({
colorScheme,
}: {
Expand Down
7 changes: 4 additions & 3 deletions src/services/wallpaper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import bg3 from "@/assets/images/background_3.jpg";
import bg4 from "@/assets/images/background_4.jpg";
import bg5 from "@/assets/images/background_5.jpg";
import { get } from "@/utils/httpClient";
import { UNSPLASH_API_KEY, UNSPLASH_URL } from '@env';
import { BackgroundQuery, Uri } from "./types";

const { EXPO_PUBLIC_UNSPLASH_URL, EXPO_PUBLIC_UNSPLASH_API_KEY } = process.env;

const PER_PAGE = 3; // retrieves 3 wallpaper and choose one of them
const ORIENTATION = "portrait";
const BACKGROUND_LIST = [bg1, bg2, bg3, bg4, bg5];

export const getLocationBackground = async ({
query,
}: BackgroundQuery): Promise<Uri | null> => {
const data = await get(`${UNSPLASH_URL}/search/photos`, {
const data = await get(`${EXPO_PUBLIC_UNSPLASH_URL}/search/photos`, {
query,
per_page: PER_PAGE,
orientation: ORIENTATION,
client_id: UNSPLASH_API_KEY,
client_id: EXPO_PUBLIC_UNSPLASH_API_KEY,
});

if (data.total === 0) {
Expand Down
42 changes: 22 additions & 20 deletions src/services/weather/index.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
import { get } from "@/utils/httpClient";
import { OPENWEATHER_API_KEY, OPENWEATHER_URL } from "@env";
import { formatMilliseconds } from "./time-formatter";
import { Coordinates, Forecast, Weather } from "./types";

const FORECAST_MAX_HOURS = 5;
const UNIT_SYSTEM = "metric";

const { EXPO_PUBLIC_OPENWEATHER_API_KEY, EXPO_PUBLIC_OPENWEATHER_URL } =
process.env;

type DeviceProps = {
device: {
locale: string,
timeZone?: string,
}
locale: string;
timeZone?: string;
};
};

type CreateQueryProps = {
location?: string;
coordinates?: Coordinates;
}
};

const createQuery = ({ location, coordinates }: CreateQueryProps) => {
return location
? { q: location }
: {
lat: coordinates?.latitude,
lon: coordinates?.longitude,
};
lat: coordinates?.latitude,
lon: coordinates?.longitude,
};
};

type GetWeatherProps = DeviceProps & CreateQueryProps;
export const getCurrentWeather = async ({
location,
coordinates,
device
device,
}: GetWeatherProps): Promise<Weather> => {
if (!location && !coordinates?.latitude && !coordinates?.longitude) {
throw new Error("Either location or coordinates must be provided");
}

const data = await get(`${OPENWEATHER_URL}/data/2.5/weather`, {
const data = await get(`${EXPO_PUBLIC_OPENWEATHER_URL}/data/2.5/weather`, {
...createQuery({ location, coordinates }),
units: UNIT_SYSTEM,
lang: device.locale,
appid: OPENWEATHER_API_KEY,
appid: EXPO_PUBLIC_OPENWEATHER_API_KEY,
});

return {
Expand All @@ -62,17 +64,17 @@ export const getCurrentWeather = async ({
now: formatMilliseconds({
time: (data.dt + data.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
sunrise: formatMilliseconds({
time: (data.sys.sunrise + data.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
sunset: formatMilliseconds({
time: (data.sys.sunset + data.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
},
};
Expand All @@ -82,34 +84,34 @@ type GetHourlyForecastProps = DeviceProps & CreateQueryProps;
export const getHourlyForecast = async ({
location,
coordinates,
device
device,
}: GetHourlyForecastProps): Promise<Forecast> => {
const data = await get(`${OPENWEATHER_URL}/data/2.5/forecast`, {
const data = await get(`${EXPO_PUBLIC_OPENWEATHER_URL}/data/2.5/forecast`, {
...createQuery({ location, coordinates }),
units: UNIT_SYSTEM,
lang: device.locale,
appid: OPENWEATHER_API_KEY,
appid: EXPO_PUBLIC_OPENWEATHER_API_KEY,
});

return {
hours: data.list.slice(0, FORECAST_MAX_HOURS).map((f: any) => ({
time: formatMilliseconds({
time: (f.dt + data.city.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
temperature: f.main.temp,
condition: f.weather[0].main,
})),
sunrise: formatMilliseconds({
time: (data.city.sunrise + data.city.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
sunset: formatMilliseconds({
time: (data.city.sunset + data.city.timezone) * 1000,
locale: device.locale,
timeZone: device.timeZone
timeZone: device.timeZone,
}),
};
};
8 changes: 4 additions & 4 deletions tests/openweather.test.http
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
# Get Weather at coordinates
# @prompt lat Latitude coordinate
# @prompt lon Longitude coordinate
GET {{$dotenv OPENWEATHER_URL}}/data/2.5/weather?lat={{lat}}&lon={{lon}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv OPENWEATHER_API_KEY}} HTTP/1.1
GET {{$dotenv EXPO_PUBLIC_OPENWEATHER_URL}}/data/2.5/weather?lat={{lat}}&lon={{lon}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv EXPO_PUBLIC_EXPO_PUBLIC_OPENWEATHER_API_KEY}} HTTP/1.1

###
# Get Weather at location string
# @prompt location Location string
GET {{$dotenv OPENWEATHER_URL}}/data/2.5/weather?q={{location}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv OPENWEATHER_API_KEY}} HTTP/1.1
GET {{$dotenv EXPO_PUBLIC_OPENWEATHER_URL}}/data/2.5/weather?q={{location}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv EXPO_PUBLIC_OPENWEATHER_API_KEY}} HTTP/1.1

###
# Get Forecast at coordinates
# @prompt lat Latitude coordinate
# @prompt lon Longitude coordinate
GET {{$dotenv OPENWEATHER_URL}}/data/2.5/forecast?lat={{lat}}&lon={{lon}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv OPENWEATHER_API_KEY}} HTTP/1.1
GET {{$dotenv EXPO_PUBLIC_OPENWEATHER_URL}}/data/2.5/forecast?lat={{lat}}&lon={{lon}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv EXPO_PUBLIC_OPENWEATHER_API_KEY}} HTTP/1.1

###
# Get Forecast at location string
# @prompt location Location string
GET {{$dotenv OPENWEATHER_URL}}/data/2.5/forecast?q={{location}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv OPENWEATHER_API_KEY}} HTTP/1.1
GET {{$dotenv EXPO_PUBLIC_OPENWEATHER_URL}}/data/2.5/forecast?q={{location}}&units={{unitSystem}}&lang={{lang}}&appid={{$dotenv EXPO_PUBLIC_OPENWEATHER_API_KEY}} HTTP/1.1
2 changes: 1 addition & 1 deletion tests/unsplash.test.http
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
@perPage = 3
@orientation = portrait

GET {{$dotenv UNSPLASH_URL}}/search/photos?query={{query}}&per_page={{perPage}}&orientation={{orientation}}&client_id={{$dotenv UNSPLASH_API_KEY}} HTTP/1.1
GET {{$dotenv EXPO_PUBLIC_UNSPLASH_URL}}/search/photos?query={{query}}&per_page={{perPage}}&orientation={{orientation}}&client_id={{$dotenv EXPO_PUBLIC_UNSPLASH_API_KEY}} HTTP/1.1
12 changes: 0 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5689,11 +5689,6 @@ dotenv-expand@~11.0.6:
dependencies:
dotenv "^16.4.4"

dotenv@^16.3.1:
version "16.4.1"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.1.tgz#1d9931f1d3e5d2959350d1250efab299561f7f11"
integrity sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==

dotenv@^16.4.4, dotenv@~16.4.5:
version "16.4.5"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f"
Expand Down Expand Up @@ -9766,13 +9761,6 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==

react-native-dotenv@^3.4.9:
version "3.4.9"
resolved "https://registry.yarnpkg.com/react-native-dotenv/-/react-native-dotenv-3.4.9.tgz#621c5b0c1d0c5c7f569bfe5a1d804bec7885c010"
integrity sha512-dbyd+mcy7SUzxEgmt33TRf1FGcNe6swJhXmB0unKkI49F7+pidog9kPtjxMLTAfmKA8gcN2XHQSKltGfGbGCLQ==
dependencies:
dotenv "^16.3.1"

[email protected]:
version "4.8.2"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.8.2.tgz#e6b3d8acf3c6afcb4b5db03a97f9c37df7668f65"
Expand Down

0 comments on commit 91bf9f2

Please sign in to comment.