Skip to content

Commit

Permalink
Merged PR 31238: Per omgeving aparte API key in de app
Browse files Browse the repository at this point in the history
Related work items: #121984, #121985
  • Loading branch information
RikSchefferAmsterdam committed Aug 15, 2024
2 parents a2fed28 + fa44897 commit 35a96b7
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 30 deletions.
7 changes: 5 additions & 2 deletions .docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ Azure DevOps Pipelines has a library where our secrets are stored. This consists
### Variables

- `KEYSTORE_PASSWORD`: used in the pipelines: password to decrypt the Android upload key; used by Fastlane
- `API_KEY`: authorize api requests; used in the app as env var
- `API_KEY_DEV`: authorize api requests for development environment; used in the app as env var
- `API_KEY_TEST`: authorize api requests for test environment; used in the app as env var
- `API_KEY_ACC`: authorize api requests for acceptance environment; used in the app as env var
- `API_KEY_PROD`: authorize api requests for production environment; used in the app as env var
- `PIWIK_PRO_URL`, `PIWIK_PRO_URL_ACCEPT`: Piwik Pro API URL for production or accept
- `PIWIK_PRO_ID`, `PIWIK_PRO_ID_ACCEPT`: Piwik Pro site ID for production or accept
- `MATCH_GIT_BEARER_AUTHORIZATION`: token used by Fastlane Match to access the aapp_app_ios-certificates repo
- `MATCH_PASSWORD`: a password used to encrypt and decrypt the certificates in the aapp_app_ios-certificates repo
- `APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_DEV`: application insights instrumentation key for development
- `APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_TEST`: application insights instrumentation key for test
- `APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_ACC`: application insights instrumentation key for acceptation
- `APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_ACC`: application insights instrumentation key for acceptance
- `APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_PROD`: application insights instrumentation key for production

### Secure files
Expand Down
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# When modifying these vars, please keep the @env typings in src/custom.d.ts, the allowlist in babel.config.js and the globals in jest.config.js up to date!

# Backend API key
API_KEY=
# Backend API keys
API_KEY_DEV=
API_KEY_TEST=
API_KEY_ACC=
API_KEY_PROD=

# Version and build number (generated during the build in DevOps)
VERSION=0.0.0
Expand Down
5 changes: 4 additions & 1 deletion .storybook/mocks/env/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const API_KEY = ''
export const API_KEY_DEV = ''
export const API_KEY_TEST = ''
export const API_KEY_ACC = ''
export const API_KEY_PROD = ''
export const VERSION = '0.0.0'
export const BUILD_NUMBER = '0'
export const PIWIK_PRO_URL = ''
Expand Down
23 changes: 21 additions & 2 deletions android/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ apps = [
:distribute_to_stakeholders => false,
:distribute_to_production => false,
:apk_path => "/apk/dev/release/app-dev-release.apk",
:aab_path => "/bundle/devRelease/app-dev-release.aab"
:aab_path => "/bundle/devRelease/app-dev-release.aab",
:env => {
API_KEY_DEV => ENV["FILTER_API_KEY_DEV"],
API_KEY_TEST => ENV["FILTER_API_KEY_TEST"],
API_KEY_ACC => ENV["FILTER_API_KEY_ACC"],
API_KEY_PROD => ENV["FILTER_API_KEY_PROD"],
},
},
{
:name => "amsterdam",
Expand All @@ -47,7 +53,13 @@ apps = [
:distribute_to_stakeholders => true,
:distribute_to_production => true,
:apk_path => "/apk/prod/release/app-prod-release.apk",
:aab_path => "/bundle/prodRelease/app-prod-release.aab"
:aab_path => "/bundle/prodRelease/app-prod-release.aab",
:env => {
API_KEY_DEV => "",
API_KEY_TEST => "",
API_KEY_ACC => "",
API_KEY_PROD => ENV["FILTER_API_KEY_PROD"],
},
},
]

Expand Down Expand Up @@ -85,6 +97,12 @@ end

platform :android do

lane :setEnvironmentForApp do |app|
app.env.each do |key, value|
ENV[key] = value
end
end

desc "Build dev and prod version of the app"
lane :buildApps do
keystore_path = "#{File.expand_path(File.dirname(__FILE__))}/../certs/upload.keystore"
Expand All @@ -96,6 +114,7 @@ platform :android do
system("keytool -list -v -keystore #{keystore_path} -storepass #{keystore_password}") # this is the upload certificate which should be used to sign the apk/aab

apps.each do |app|
setEnvironmentForApp(app)
gradle(
task: app[:task],
build_type: app[:build_type],
Expand Down
5 changes: 4 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ module.exports = {
moduleName: '@env',
path: '.env',
allowlist: [
'API_KEY',
'API_KEY_DEV',
'API_KEY_TEST',
'API_KEY_ACC',
'API_KEY_PROD',
'BUILD_NUMBER',
'PIWIK_PRO_ID',
'PIWIK_PRO_ID_ACCEPT',
Expand Down
19 changes: 19 additions & 0 deletions ios/fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ apps = [
:distribute_to_stakeholders => false,
:distribute_to_production => false,
:output_name => "AmsterdamTest.ipa",
:env => {
API_KEY_DEV => ENV["FILTER_API_KEY_DEV"],
API_KEY_TEST => ENV["FILTER_API_KEY_TEST"],
API_KEY_ACC => ENV["FILTER_API_KEY_ACC"],
API_KEY_PROD => ENV["FILTER_API_KEY_PROD"],
},
},
{
:name => "amsterdam",
Expand All @@ -39,6 +45,12 @@ apps = [
:distribute_to_stakeholders => true,
:distribute_to_production => true,
:output_name => "Amsterdam.ipa",
:env => {
API_KEY_DEV => "",
API_KEY_TEST => "",
API_KEY_ACC => "",
API_KEY_PROD => ENV["FILTER_API_KEY_PROD"],
},
},
]

Expand Down Expand Up @@ -150,6 +162,12 @@ platform :ios do
syncAppstore(options)
end

lane :setEnvironmentForApp do |app|
app.env.each do |key, value|
ENV[key] = value
end
end

desc "Build prod and dev version of the app"
lane :buildApps do

Expand Down Expand Up @@ -189,6 +207,7 @@ platform :ios do

apps.each do |app|
puts "Build #{app[:app_identifier]}"
setEnvironmentForApp(app)
if app[:name] == "amsterdam-test"
devLane(app)
else
Expand Down
5 changes: 4 additions & 1 deletion jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const config: Config = {
'node_modules/(?!(jest-)?react-native|@react-native|@notifee)',
],
globals: {
API_KEY: '',
API_KEY_DEV: '',
API_KEY_TEST: '',
API_KEY_ACC: '',
API_KEY_PROD: '',
VERSION: '0.0.0',
BUILD_NUMBER: 0,
PIWIK_PRO_URL: '',
Expand Down
5 changes: 4 additions & 1 deletion pipelines/templates/app-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,14 @@ steps:
FASTLANE_XCODE_LIST_TIMEOUT: '180'
MATCH_GIT_BEARER_AUTHORIZATION: $(System.AccessToken)
MATCH_PASSWORD: $(MATCH_PASSWORD)
API_KEY: $(API_KEY)
APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_DEV: $(APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_DEV)
APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_TEST: $(APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_TEST)
APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_ACC: $(APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_ACC)
APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_PROD: $(APPLICATION_INSIGHTS_INSTRUMENTATION_KEY_PROD)
FILTER_API_KEY_DEV: $(API_KEY_DEV)
FILTER_API_KEY_TEST: $(API_KEY_TEST)
FILTER_API_KEY_ACC: $(API_KEY_ACC)
FILTER_API_KEY_PROD: $(API_KEY_PROD)
# Handle artifacts
- template: ./copy-generic-artifacts.yml
Expand Down
5 changes: 4 additions & 1 deletion src/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ declare module '*.png' {
}

declare module '@env' {
export const API_KEY: string | undefined
export const API_KEY_DEV: string | undefined
export const API_KEY_TEST: string | undefined
export const API_KEY_ACC: string | undefined
export const API_KEY_PROD: string | undefined
export const BUILD_NUMBER: string | undefined
export const PIWIK_PRO_ID: string | undefined
export const PIWIK_PRO_ID_ACCEPT: string | undefined
Expand Down
8 changes: 6 additions & 2 deletions src/environment.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import {getApi, Environment, GlobalApiSlug} from '@/environment'
import {
getApi,
Environment,
GlobalApiSlug,
customDefaultUrls,
} from '@/environment'
import {ModuleSlug} from '@/modules/slugs'
import {customDefaultUrls} from '@/store/slices/environment'

describe('getApi', () => {
test('returns correct api url for development environment', () => {
Expand Down
19 changes: 17 additions & 2 deletions src/environment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable typescript-sort-keys/string-enum */
import {API_KEY_ACC, API_KEY_DEV, API_KEY_PROD, API_KEY_TEST} from '@env'
import {ModuleSlug} from '@/modules/slugs'
import {customDefaultUrls} from '@/store/slices/environment'

export enum Environment {
development = 'Development',
Expand Down Expand Up @@ -46,7 +46,7 @@ export const getApi = (
slug: ApiSlug,
apiVersionPath = '/api/v1',
) => {
if (environment === Environment.custom && slug in custom) {
if (environment === Environment.custom && slug in (custom ?? {})) {
return custom[slug as keyof typeof customDefaultUrls]
}

Expand All @@ -55,3 +55,18 @@ export const getApi = (

return `https://${env}${interPunction}app.amsterdam.nl/${slug}${apiVersionPath}`
}

export const customDefaultUrls = {
[editableApiSlug.constructionWork]:
'http://localhost:8000/construction-work/api/v1',
[editableApiSlug.contact]: 'http://localhost:8000/contact/api/v1',
[editableApiSlug.modules]: 'http://localhost:9000/modules/api/v1',
}

export const apiKeyForEnvironment: EnvUrlMap = {
[Environment.development]: API_KEY_DEV ?? '',
[Environment.test]: API_KEY_TEST ?? '',
[Environment.acceptance]: API_KEY_ACC ?? '',
[Environment.production]: API_KEY_PROD ?? '',
[Environment.custom]: API_KEY_DEV ?? '',
}
19 changes: 12 additions & 7 deletions src/services/baseApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {API_KEY} from '@env'
import {
BaseQueryFn,
createApi,
Expand All @@ -7,11 +6,11 @@ import {
FetchBaseQueryError,
retry,
} from '@reduxjs/toolkit/query/react'
import {ApiSlug} from '@/environment'
import {apiKeyForEnvironment, ApiSlug} from '@/environment'
import {ProjectsEndpointName} from '@/modules/construction-work/types/api'
import {devError, devInfo} from '@/processes/development'
import {PrepareHeaders, AfterBaseQueryFn} from '@/services/types'
import {selectApi} from '@/store/slices/environment'
import {selectApi, selectEnvironment} from '@/store/slices/environment'
import {RootState} from '@/store/types/rootState'
import {TimeOutDuration} from '@/types/api'
import {DeviceRegistrationEndpointName} from '@/types/device'
Expand All @@ -29,14 +28,20 @@ const deviceIdRequestingEndpoints: string[] = [
DeviceRegistrationEndpointName.unregisterDevice,
]

const prepareHeaders: PrepareHeaders = (headers, {endpoint}) => {
const prepareHeaders: PrepareHeaders = (headers, {endpoint, getState}) => {
deviceIdRequestingEndpoints.includes(endpoint) &&
headers.set('deviceid', SHA256EncryptedDeviceId)

if (API_KEY) {
headers.set('X-API-KEY', API_KEY)
const state = getState() as RootState

const {environment} = selectEnvironment(state)

const apiKey = apiKeyForEnvironment[environment]

if (apiKey) {
headers.set('X-API-KEY', apiKey)
} else {
devError('No API key in .env.')
devError(`No API key in .env for environment ${environment}.`)
}

headers.set('releaseVersion', VERSION_NUMBER)
Expand Down
9 changes: 1 addition & 8 deletions src/store/slices/environment.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import {createSlice, PayloadAction} from '@reduxjs/toolkit'
import {Environment, getApi, ApiSlug, editableApiSlug} from '@/environment'
import {Environment, getApi, ApiSlug, customDefaultUrls} from '@/environment'
import {isDevApp} from '@/processes/development'
import {ReduxKey} from '@/store/types/reduxKey'
import {RootState} from '@/store/types/rootState'

export const customDefaultUrls = {
[editableApiSlug.constructionWork]:
'http://localhost:8000/construction-work/api/v1',
[editableApiSlug.contact]: 'http://localhost:8000/contact/api/v1',
[editableApiSlug.modules]: 'http://localhost:9000/modules/api/v1',
}

export type EnvironmentState = {
custom: typeof customDefaultUrls
environment: Environment
Expand Down

0 comments on commit 35a96b7

Please sign in to comment.