Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pingdom API integration #110

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,205 changes: 4,205 additions & 0 deletions server/@types/pingdom.d.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import serviceAreaRoutes from './routes/serviceAreas'
import monitorRoutes from './routes/monitor'
import dependencyRoutes from './routes/dependencies'
import driftRadiatorRoutes from './routes/driftRadiator'
import pingdom from './routes/pingdom'
import teamHealthRoutes from './routes/teamHealth'
import missingFromCatalogueRoutes from './routes/missingFromCatalogue'
import namespacesRoutes from './routes/namespaces'
Expand Down Expand Up @@ -54,6 +55,7 @@ export default function createApp(services: Services): express.Application {
app.use('/monitor', monitorRoutes(services))
app.use('/dependencies', dependencyRoutes(services))
app.use('/drift-radiator', driftRadiatorRoutes(services))
app.use('/pingdom', pingdom(services))
app.use('/team-health', teamHealthRoutes(services))
app.use('/product-dependencies', productDependencyRoutes(services))
app.use('/missing-from-catalogue', missingFromCatalogueRoutes(services))
Expand Down
10 changes: 10 additions & 0 deletions server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface ApiConfig {
deadline: number
}
agent: AgentConfig
token: string
}

export default {
Expand Down Expand Up @@ -57,6 +58,15 @@ export default {
},
agent: new AgentConfig(Number(get('SERVICE_CATALOGUE_TIMEOUT_RESPONSE', 5000))),
},
pingdom: {
url: get('PINGDOM_API', 'https://api.pingdom.com/api/3.1/', requiredInProduction),
timeout: {
response: Number(get('PINGDOM_API_TIMEOUT_RESPONSE', 5000)),
deadline: Number(get('PINGDOM_API_TIMEOUT_DEADLINE', 5000)),
},
agent: new AgentConfig(Number(get('PINGDOM_API_TIMEOUT_RESPONSE', 5000))),
token: get('PINGDOM_TOKEN', '', requiredInProduction),
},
},
domain: get('INGRESS_URL', 'http://localhost:3000', requiredInProduction),
}
5 changes: 4 additions & 1 deletion server/data/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ buildAppInsightsClient(applicationInfo)

import StrapiApiClient from './strapiApiClient'

import PingdomApiClient from './pingdomApiClient'

type RestClientBuilder<T> = (token: string) => T

export const dataAccess = () => ({
applicationInfo,
strapiApiClientBuilder: (() => new StrapiApiClient()) as RestClientBuilder<StrapiApiClient>,
pingdomApiClientBuilder: (() => new PingdomApiClient()) as RestClientBuilder<PingdomApiClient>,
})

export type DataAccess = ReturnType<typeof dataAccess>

export { StrapiApiClient, RestClientBuilder }
export { PingdomApiClient, StrapiApiClient, RestClientBuilder }
17 changes: 17 additions & 0 deletions server/data/pingdomApiClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import RestClient from './restClient'
import config, { ApiConfig } from '../config'
import { Checks } from './pingdomApiTypes'

export default class PingdomApiClient {
private restClient: RestClient

constructor() {
this.restClient = new RestClient('pingdomApiClient', config.apis.pingdom as ApiConfig)
}

async getChecks(): Promise<Checks> {
return this.restClient.get({
path: '/checks',
})
}
}
4 changes: 4 additions & 0 deletions server/data/pingdomApiTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { components } from '../@types/pingdom'

export type Check = components['schemas']['Check']
export type Checks = components['schemas']['Checks']
11 changes: 7 additions & 4 deletions server/data/restClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export default class RestClient {
constructor(
private readonly name: string,
private readonly config: ApiConfig,
private readonly token: string,
) {
this.agent = config.url.startsWith('https') ? new HttpsAgent(config.agent) : new Agent(config.agent)
}
Expand All @@ -48,6 +47,10 @@ export default class RestClient {
return this.config.timeout
}

private token() {
return this.config.token
}

async get({ path = null, query = '', headers = {}, responseType = '', raw = false }: GetRequest): Promise<unknown> {
logger.info(`Get calling ${this.name}: ${path} ${query}`)
try {
Expand All @@ -59,7 +62,7 @@ export default class RestClient {
return undefined // retry handler only for logging retries, not to influence retry logic
})
.query(query)
.auth(this.token, { type: 'bearer' })
.auth(this.token(), { type: 'bearer' })
.set(headers)
.responseType(responseType)
.timeout(this.timeoutConfig())
Expand Down Expand Up @@ -89,7 +92,7 @@ export default class RestClient {
if (err) logger.info(`Retry handler found API error with ${err.code} ${err.message}`)
return undefined // retry handler only for logging retries, not to influence retry logic
})
.auth(this.token, { type: 'bearer' })
.auth(this.token(), { type: 'bearer' })
.set(headers)
.responseType(responseType)
.timeout(this.timeoutConfig())
Expand All @@ -108,7 +111,7 @@ export default class RestClient {
superagent
.get(`${this.apiUrl()}${path}`)
.agent(this.agent)
.auth(this.token, { type: 'bearer' })
.auth(this.token(), { type: 'bearer' })
.retry(2, (err, res) => {
if (err) logger.info(`Retry handler found API error with ${err.code} ${err.message}`)
return undefined // retry handler only for logging retries, not to influence retry logic
Expand Down
2 changes: 1 addition & 1 deletion server/data/strapiApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class StrapiApiClient {
private restClient: RestClient

constructor() {
this.restClient = new RestClient('strapiApiClient', config.apis.serviceCatalogue as ApiConfig, '')
this.restClient = new RestClient('strapiApiClient', config.apis.serviceCatalogue as ApiConfig)
}

async getProducts({
Expand Down
17 changes: 17 additions & 0 deletions server/routes/pingdom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { type RequestHandler, Router } from 'express'
import asyncMiddleware from '../middleware/asyncMiddleware'
import type { Services } from '../services'

export default function routes({ pingdomService }: Services): Router {
const router = Router()

const get = (path: string, handler: RequestHandler) => router.get(path, asyncMiddleware(handler))

get('/', async (req, res) => {
const pingdomChecks = await pingdomService.getChecks()

return res.render('pages/pingdom', { checks: pingdomChecks })
})

return router
}
5 changes: 4 additions & 1 deletion server/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { dataAccess } from '../data'
import ServiceCatalogueService from './serviceCatalogueService'
import PingdomService from './pingdomService'
import ProductDependenciesService from './productDependenciesService'
import RedisService from './redisService'
import { createRedisClient } from '../data/redisClient'
Expand All @@ -9,11 +10,12 @@ import DataFilterService from './dataFilterService'
import TeamHealthService from './teamHealthService'

export const services = () => {
const { strapiApiClientBuilder, applicationInfo } = dataAccess()
const { pingdomApiClientBuilder, strapiApiClientBuilder, applicationInfo } = dataAccess()
const client = createRedisClient()
client.connect().catch((err: Error) => logger.error(`Error connecting to Redis`, err))

const serviceCatalogueService = new ServiceCatalogueService(strapiApiClientBuilder)
const pingdomService = new PingdomService(pingdomApiClientBuilder)
const componentNameService = new ComponentNameService(strapiApiClientBuilder)
const redisService = new RedisService(client)
const productDependenciesService = new ProductDependenciesService(strapiApiClientBuilder, redisService)
Expand All @@ -23,6 +25,7 @@ export const services = () => {
return {
applicationInfo,
serviceCatalogueService,
pingdomService,
componentNameService,
redisService,
dataFilterService,
Expand Down
13 changes: 13 additions & 0 deletions server/services/pingdomService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { PingdomApiClient, RestClientBuilder } from '../data'
import { Checks } from '../data/pingdomApiTypes'

export default class PingdomService {
constructor(private readonly pingdomApiClientFactory: RestClientBuilder<PingdomApiClient>) {}

async getChecks(): Promise<Checks> {
const pingdomApiClient = this.pingdomApiClientFactory('')
const checks = await pingdomApiClient.getChecks()

return checks
}
}
18 changes: 18 additions & 0 deletions server/views/pages/pingdom.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "../partials/layout.njk" %}

{% set pageTitle = applicationName %}
{% set mainClasses = "app-container govuk-body" %}

{% block content %}

<h1 id="detailPageTitle">Pingdom</h1>

{{ checks | dump | safe }}

{% endblock %}

{% block bodyEnd %}
<script src="/assets/govuk/all.js"></script>
<script src="/assets/govukFrontendInit.js"></script>
<script src="/assets/moj/all.js"></script>
{% endblock %}