Skip to content

Commit

Permalink
Fetch certificate configuration from country config (#6165)
Browse files Browse the repository at this point in the history
* Load fonts from country-config

* Cache font & cert config in service worker

* Update cache routes for validators, handlebars etc

* Use NotoSans as fallback for backward compatibility

* Remove auth header from cert config request

* Add retry on load failed

* Provide mock for the fonts

* Mock loadCertificateConfiguration function

* Remove dev certificate handler

---------

Co-authored-by: euanmillar <euanmillar77@gmail.com>
  • Loading branch information
Zangetsu101 and euanmillar authored Nov 8, 2023
1 parent a04614c commit 56a9c54
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 16 deletions.
34 changes: 33 additions & 1 deletion packages/client/src/offline/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
LoadFormsResponse,
LoadValidatorsResponse,
LoadConditionalsResponse,
LoadHandlebarHelpersResponse
LoadHandlebarHelpersResponse,
CertificateConfiguration
} from '@client/utils/referenceApi'
import { System } from '@client/utils/gateway'
import { UserDetails } from '@client/utils/userUtils'
Expand Down Expand Up @@ -125,6 +126,21 @@ type CertificatesLoadFailedAction = {
type: typeof CERTIFICATES_LOAD_FAILED
payload: Error
}

export const CERTIFICATE_CONFIGURATION_LOADED =
'OFFLINE/CERTIFICATE_CONFIGURATION_LOADED'
type CertificateConfigurationLoadedAction = {
type: typeof CERTIFICATE_CONFIGURATION_LOADED
payload: CertificateConfiguration
}

export const CERTIFICATE_CONFIGURATION_LOAD_FAILED =
'OFFLINE/CERTIFICATE_CONFIGURATION_LOAD_FAILED'
type CertificateConfigurationLoadFailedAction = {
type: typeof CERTIFICATE_CONFIGURATION_LOAD_FAILED
payload: Error
}

export const UPDATE_OFFLINE_CONFIG = 'OFFLINE/UPDATE_OFFLINE_CONFIG' as const
type ApplicationConfigUpdatedAction = {
type: typeof UPDATE_OFFLINE_CONFIG
Expand Down Expand Up @@ -275,6 +291,20 @@ export const certificatesLoaded = (
payload
})

export const certificateConfigurationLoaded = (
payload: CertificateConfiguration
): CertificateConfigurationLoadedAction => ({
type: CERTIFICATE_CONFIGURATION_LOADED,
payload
})

export const certificateConfigurationLoadFailed = (
payload: CertificateConfigurationLoadFailedAction['payload']
): CertificateConfigurationLoadFailedAction => ({
type: CERTIFICATE_CONFIGURATION_LOAD_FAILED,
payload
})

export const configFailed = (error: Error): ApplicationConfigFailedAction => ({
type: APPLICATION_CONFIG_FAILED,
payload: error
Expand Down Expand Up @@ -368,6 +398,8 @@ export type Action =
| CertificateLoadFailedAction
| CertificatesLoadedAction
| CertificatesLoadFailedAction
| CertificateConfigurationLoadedAction
| CertificateConfigurationLoadFailedAction
| UpdateOfflineSystemsAction
| UpdateOfflineCertificateAction
| IFilterLocationsAction
Expand Down
31 changes: 30 additions & 1 deletion packages/client/src/offline/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
IApplicationConfigAnonymous,
ILocationDataResponse,
ICertificateTemplateData,
referenceApi
referenceApi,
CertificateConfiguration
} from '@client/utils/referenceApi'
import { ILanguage } from '@client/i18n/reducer'
import { filterLocations } from '@client/utils/locationUtils'
Expand Down Expand Up @@ -82,6 +83,7 @@ export interface IOfflineData {
languages: ILanguage[]
templates: {
receipt?: IPDFTemplate
fonts?: CertificateConfiguration['fonts']
// Certificates might not be defined in the case of
// a field agent using the app.
certificates?: {
Expand Down Expand Up @@ -222,6 +224,14 @@ const CONFIG_CMD = Cmd.run(() => referenceApi.loadConfig(), {
failActionCreator: actions.configFailed
})

const CERTIFICATE_CONFIG_CMD = Cmd.run(
() => referenceApi.loadCertificateConfiguration(),
{
successActionCreator: actions.certificateConfigurationLoaded,
failActionCreator: actions.certificateConfigurationLoadFailed
}
)

const CONTENT_CMD = Cmd.run(() => referenceApi.loadContent(), {
successActionCreator: actions.contentLoaded,
failActionCreator: actions.contentFailed
Expand Down Expand Up @@ -256,6 +266,7 @@ function getDataLoadingCommands() {
FACILITIES_CMD,
LOCATIONS_CMD,
CONFIG_CMD,
CERTIFICATE_CONFIG_CMD,
CONDITIONALS_CMD,
VALIDATORS_CMD,
HANDLEBARS_CMD,
Expand Down Expand Up @@ -520,6 +531,7 @@ function reducer(
const newOfflineData = {
...state.offlineData,
templates: {
...state.offlineData.templates,
certificates: certificatesTemplates
}
}
Expand Down Expand Up @@ -581,6 +593,23 @@ function reducer(
)
}

case actions.CERTIFICATE_CONFIGURATION_LOADED: {
return {
...state,
offlineData: {
...state.offlineData,
templates: {
...state.offlineData.templates,
fonts: action.payload.fonts
}
}
}
}

case actions.CERTIFICATE_CONFIGURATION_LOAD_FAILED: {
return loop(state, delay(CERTIFICATE_CONFIG_CMD, RETRY_TIMEOUT))
}

/*
* Locations
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/pdfRenderer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export function createPDF(
return pdfMake.createPdf(
JSON.parse(definitionString),
undefined,
template.fonts[intl.locale] || template.fonts[intl.defaultLocale]
template.fonts
)
}
/*
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/pdfRenderer/transformer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type OptionalData = IAvailableCountries[]

export interface IPDFTemplate {
definition: TDocumentDefinitions
fonts: { [language: string]: { [name: string]: TFontFamilyTypes } }
fonts: Record<string, TFontFamilyTypes>
vfs?: { [file: string]: string }
transformers?: IFieldTransformer[]
}
Expand Down
1 change: 1 addition & 0 deletions packages/client/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ vi.doMock(
languages: mockOfflineData.languages
}),
loadConfig: () => Promise.resolve(mockConfigResponse),
loadCertificateConfiguration: () => Promise.resolve({}),
loadConfigAnonymousUser: () => Promise.resolve(mockConfigResponse),
loadForms: () => Promise.resolve(mockOfflineData.forms.forms),
importConditionals: () => Promise.resolve({}),
Expand Down
10 changes: 9 additions & 1 deletion packages/client/src/src-sw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,16 @@ cleanupOutdatedCaches()

// This caches the config files fetched from country config
registerRoute(/http(.+)config\.js$/, new NetworkFirst())
// This caches the certificate config file fetched from country config
registerRoute(/http(.+)certificate-configuration$/, new NetworkFirst())
// This caches font files fetched from country config
registerRoute(/http(.+)fonts\/.*\.ttf$/, new NetworkFirst())
// This caches validations fetched from country config
registerRoute(/http(.+)validation\.js$/, new NetworkFirst())
registerRoute(/http(.+)validators\.js$/, new NetworkFirst())
// This caches handlebars fetched from country config
registerRoute(/http(.+)handlebars\.js$/, new NetworkFirst())
// This caches conditionals fetched from country config
registerRoute(/http(.+)conditionals\.js$/, new NetworkFirst())
// This caches config fetched from the config microservice
registerRoute(/http(.+)config$/, new NetworkFirst())

Expand Down
11 changes: 1 addition & 10 deletions packages/client/src/templates/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,5 @@ export const certificateBaseTemplate = {
},
content: []
},
fonts: {
en: {
notosans: {
normal: 'NotoSans-Light.ttf',
bold: 'NotoSans-Regular.ttf',
italics: 'NotoSans-Light.ttf',
bolditalics: 'NotoSans-Regular.ttf'
}
}
}
fonts: {}
}
8 changes: 8 additions & 0 deletions packages/client/src/tests/templates.json

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions packages/client/src/utils/referenceApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ export interface ILocationDataResponse {
export interface IFacilitiesDataResponse {
[facilityId: string]: ILocation
}

type FontFamilyTypes = {
normal: string
bold: string
italics: string
bolditalics: string
}

export type CertificateConfiguration = Partial<{
fonts: Record<string, FontFamilyTypes>
}>
export interface IContentResponse {
languages: ILanguage[]
}
Expand Down Expand Up @@ -224,6 +235,33 @@ async function importHandlebarHelpers(): Promise<LoadHandlebarHelpersResponse> {
return {}
}
}
async function loadCertificateConfiguration(): Promise<CertificateConfiguration> {
const url = `${window.config.COUNTRY_CONFIG_URL}/certificate-configuration`

const res = await fetch(url, {
method: 'GET'
})

// for backward compatibility, if the endpoint is unimplemented
if (res.status === 404) {
return {
fonts: {
notosans: {
normal: 'NotoSans-Light.ttf',
bold: 'NotoSans-Regular.ttf',
italics: 'NotoSans-Light.ttf',
bolditalics: 'NotoSans-Regular.ttf'
}
}
}
}

if (!res.ok) {
throw Error(res.statusText)
}

return res.json()
}

async function loadContent(): Promise<IContentResponse> {
const url = `${window.config.COUNTRY_CONFIG_URL}/content/client`
Expand Down Expand Up @@ -362,6 +400,7 @@ async function loadFacilities(): Promise<IFacilitiesDataResponse> {
export const referenceApi = {
loadLocations,
loadFacilities,
loadCertificateConfiguration,
loadContent,
loadConfig,
loadForms,
Expand Down
8 changes: 7 additions & 1 deletion packages/client/src/views/PrintCertificate/PDFUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,13 @@ async function getPDFTemplateWithSVG(
state
)

const pdfTemplate: IPDFTemplate = certificateBaseTemplate
const pdfTemplate: IPDFTemplate = {
...certificateBaseTemplate,
fonts: {
...certificateBaseTemplate.fonts,
...offlineResource.templates.fonts
}
}
pdfTemplate.definition.pageSize = pageSize
updatePDFTemplateWithSVGContent(pdfTemplate, svgCode, pageSize)
return pdfTemplate
Expand Down

0 comments on commit 56a9c54

Please sign in to comment.