-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #349 from DEFRA/500303-make-common-octokit-config
500303 make common octokit config
- Loading branch information
Showing
13 changed files
with
244 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
// Mock private key - from https://github.com/DEFRA/cdp-local-environment/blob/9dc739301ead62a54e79a85e99b4ccb641dc7b0c/config/cdp-portal-backend.env#L5 | ||
process.env.GITHUB_API_AUTH_APP_PRIVATE_KEY = | ||
'LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBbll1ejQ1M3UwYjBkdG5NYjJUSnh1SVJMekxJS2p2VjVJMnZhem9XeGFFN3d3VnpZCnNGU2tnNzlaSGNWSEt2ZktoSXYwRWtVZmJSbzAwRGpucnRKclZkc2F1TWtNcDFjdEVHZjcyWkVpYUcyRGFNZzkKWG0yUlV4L2dKZnJlS2o2dGcybUsxd292TWQ1S3MvTjhnNXZXck8wbUdPREVYWHVDakw4MkZick9IeUNQQ1VQRApVbEtTc0xMazJnN0I5a1hHOEFsWHdjaVlmSmpMZ3JaQlBzSzhUVTh5cjA3V0ZqMTQ2S2lMN0Q5NGp3cElrNjMwCmFHekwrejZudkIzNFVFZzMwZ1RDMXRWdXkrM3JXN3Q2S0F1REJaK3hYTG9uN3dndXV4NnRoSXFGUVpRcE13SjIKSkdmNXZRNS9nSnZ4YVFQRE8zVTRtRWMxczR2b0hpNGtlNzQxczFGdjdHOENVa0lmY2dlb3lvN0E2RzNUOUs0NwpmKzNyTEhNT2szOVdudHBGa2luZ3pMVTRZK0FHVjQrZFJvV2QvemVjVndOUjcrV1lhQzJBK0JNeHlKOFhJUG9LCncvMk9IZjQ4WTQvQUIxblVSR2ZNOXpvMW5CaGFNZlJZV3ltT3BuSThxNW4rWGlkUjVCaWdmU2lmTGdSQ3RvZnAKaGNJd24wZnhGMHhLbGN4S0tTZkpoOGo4MHAvSUxYTkFzUUc5dm0zWkhxaENQc21OZkVxRUJBb1daS05kRUZ2YQpyakNkQm1hWVVONEl0bUo0NEVQWEsvTi9ScnZxMVJrRmJOd1p4cURoaVlFMm1VK2hMdjgrdXRGUWNSVTdLVWZ3ClZYd0swZkpkU3dnR0ZFMCtFb3RIY0s2Vm9lMXh6d1podThuSHI4bUs1MkwybmJ6bHBQdkJKNzRrSE44Q0F3RUEKQVFLQ0FnQXBIc2tWbkdlMG03RlJLU3M0SGdmN21xQTBMYlkreVFoVXUvWncyQWxOaWVraDl3dGh2cjN2MnpZMQo1SU5tVGlXd1FkMHpGWktWeGZUSjhraGFZM1o3Z3NRdlBkNk5JTjVVdldkSlNxM1o1dGVaTmtaNlNvdlhUK2NQCjBySkJBWG9GWmp0RVZGYXNJL0tJbElGSDBwbU9LaG02L1pPVE9NVUMybmVSNVYrZVZUK3dNZDBkdEFxd2p3algKZDJtZHoybVV4a04vQTAzMW0yWG0ySTRnQlBEa0ZzdEtZWC9VYnpnTE5jN0xtMmRxb2tyK0xMV3h1Yy9sNUYwZwpad3drWEMwaHBuZDZYbHZjWTExK1pHa0dZYmJSZFhSMEdPeUtZYTdjelMySi9pTzQwYVNOT1ZPL3ZkbTd4RGg0ClIzdVRwdFZDeEI1QTlMa3FBMWJTNWNWRS9Ra0R1eEk1cWFUYy9IcjYyVHRweFZUV0RqN0NtazhiekxodE9WNk8KM1lYODJZUHBuSVF3RDNqLytwRDhRSDFLSFdrRk42aDl2VFZZRktaQ3VqRU43LzJNNUNncWV6a2dWRmd2RVVxMQo5UGdnem9WbjZZbkdrM0hpVFZ3UHI0d0ZRcU5GQ3dxUk95VjVVOXJwTksva0Y4WEpvTlkxZHY2bjA4UmNwazE0CnYvSXV1Z2kraHFJVEN0MEpNVjdaVXlYNkJNTEM3b2lSS29JY3ZGTjJNQmxYeEdOWUJ5K2pmTVhVSG5OY1VHNncKdDdWMmFVMll4RDZ4YUJ1VVhjTFJ4MHp2VHQwZjJIZVBVK0UrRVNBNlFtZWl3RkNzNXBoOFhhVzg2VklhTWFDbwpwdzdzN2FJYTVXWGZWQVlHSFNBVG1hdEF6dzQyVjBHSmFGb0V1amhDTk03Y0EzWWdtUUtDQVFFQTJTblpQbGdHClpkaEdlZXloUTc3VHRETkZBQmpTbzJWNkZIZ1pTOWpteHlLd0ZaMEhpOHlDK0xDZDZzdTUvbW90ZlBNRG1LWSsKajlMMVlMcDRsRU5rZm5lT3F3MW1KemdKdkN5ZDZnRnZZaU03SU5IMkgxWFBPNmtMUHN5TjA0cXl2emZya1hzcgpZOG40M0lqSE9RaVNHVTRmNk82SmE2MTE2Tm90SHFzTnpnUHpkUXYvYzR3TEp4YUJxcXZ0WGJKWWRkTzYrSTFZCmFSbkVPYnVvYkxpKzVZMHlFL003OFBpWmg1aEVsczNBUDcwMmpyTkU2SUQ1OW5xM3lsOFFyMkNOUDRVdlR2SisKaSthc1kxN3gvS3hQNXgxNGt4bXo0ai9nVWF0bnF1Z3hhNVBNRElSbHUwSTJhTTh0Uks1d0lVZ2ZmM0EvV2ZXLwplY2s1WkZFVzkzZW1HUUtDQVFFQXViaHc5Q1pvTDVRM3BvUERHQ2pqckVuTndOVU43Y1IxRUlWdGVLeGlmOW82Cm9qd1ZnUXZPTERzcnVzZk1ERkNrU2pSRmIzME1DVWhmVkhXOVp0dlI5WncvTy9kazlEa1pVU0RPbS80djhjcXkKUkI2Q1lvL0x5N3pVMmU3R1dETTBSN25kS3pOVFQwOHVuQ1pzN2ZkZmRCa3JDUWI5dFY4RkpOY1pZN2YxNW1HdAp5V3QwNkt5aWVEVCtpYXJRWlUyVy9pd05DaldEZTVNcGlWVTRLQ3hkcG1rRkMwUExsMVA4b1MwQTRDMzZDYTRRCnVIYWFJT0dhWXhRM0txVmtwQlhnREJZUlhZKzhXcGFmb2lPVDhSUFk0Q2NQUEo5MVdtRy9mUVNzOGI0TlVRcU4KQmFzYjNSTGVxOTJiK2VKOXNUVktDRVZRZW9PREkyK1FrRjJJYXFHSnR3S0NBUUVBbnlBeEZhTytuR1lMems5OApJMzN5OXJvU3QyMTQrMDNpVkpsa1A5V2gxUTB2NWNaNHZ4R09idGhML3I1bGZXMGtBOGkyTythbE5SSXB4MFVjCkVkZ0lEVHRpQ1NqNlQ0YWFhNDV0OEFnNUs3b1JHNDErUVp2SkREaGtDeElzWW5QaFlvaUJUc3JvRW1qdXQvcHAKc1ByOHd4b2grN2ErYjI1ZFZkQjQrMTN2OGFPbmczN1ZJai9kOURoanIwSG4zcDVPZjVnMEN1alhmYnZwc3p3MAp2K1hueTZEWXJ4R3VQSGFOV1hSVDNnVEorR3FYVFoxQ3d2T3ZOZExhVmFtdk9qTVBqUm4wZm03bUYzRmhwRGJ3CkxtdTg2T2NKY1JDR1pTVFZKRUxxNWFyYWU2K0M5ekVVc2xCa09neUZhU3hBOGNJOWdrNG52YTE3THF3cUo3M2IKakYzYUVRS0NBUUF1ZTdaeHRVY3dXV1dRWEx3d3lOTXJKUkhGYU0zaXErQXM4V2hUNHJtSWpJTk9aY0Z3R2hkUgpSTUlPTHNHb2QycVhVL3ZwQ3FBL2xvaWRxQlp1cnlnZTFDdVRnN2tWMDFDOTJIczkyZUlBSDU1OHBESTRId3VBCkE5UTJjNmZiSFgzQlVnakVMa1YwdlRuS1JXZlFLN0VOYXRzMW1EVlo5dDFmdWlLVnJjNXpDaEdvTHlnRXNHaTkKczIzZDRRM2x2UVRFdXh2TWFWWnVVSWY5NG9GNnRKZi85WnNZbGJCWVFPSWpLUk5tQ0x2alBsamJBbnhUTElRcQp0ajJVZys3cmpyb1Q3RllPVjlKcHpmZElhcVUxbXFVV2ZWQTMrU1V6S1BIM2hYc1B5bVNrMndJdTRBUEtVbFcvCktHbElvdUtZdnVDM3J6bVVZR1FyTTFvNGQrQ1Q1N2lEQW9JQkFGdG80S1AyTWpCY2NabFEvbnBrVURFZ2p6MmkKREhLbVBHUU40WVNHTERzOUFZd3pXRmZNWjF1N1BUWlVXaXF0Q2ZpYXIzRE5BM3ZxenViYjRONVJTUm5TdEI4bQo4ZWJuRTNhaXRjMVJpUU9DL0hxYXpZN2dlekY5dUNibk9obGxuZzJEYjA1ejdrQzIwa096cU1ySzQrRXBGaXZSCkpzWEtIU2RvbHowRVFCcEpsT2Zjby9WKzlTK1FZWERlc0xUdUZMUm9yY2JSMkhVRk4zWElhRW0yYWhDZlpqbSsKL1ZFeW9wVnBZUTNsbUtDWWZrZ1pUckNTRG9wVXBZNXRGcGI4QkVYVlk0ZFVtZG5xa0loMlN2K2ZZM1cxV0pxTwoxemQvdm5LTGJMMHdsYU5mQW1IYjEweVY5V3c1aGowYUZWejFFQWh6a1BaM3RKOG45L21rWFd4TGZBYz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K' | ||
process.env.GITHUB_API_AUTH_APP_PRIVATE_KEY = 'mock-private-key' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { createAppAuth } from '@octokit/auth-app' | ||
|
||
import { removeNil } from '~/src/helpers/remove-nill.js' | ||
import { proxyFetch } from '~/src/helpers/proxy/proxy-fetch.js' | ||
|
||
/** | ||
* @typedef {object} GitHubConfig | ||
* @property {string|null} baseUrl | ||
* @property {object} app | ||
* @property {string} app.id | ||
* @property {string} app.privateKey | ||
* @property {string} app.installationId | ||
*/ | ||
|
||
/** | ||
* @typedef {import('@octokit/core').Octokit} Octokit | ||
* @typedef {import('@octokit/core').Constructor} Constructor | ||
* @typedef {import('@octokit/graphql').graphql} graphql | ||
*/ | ||
|
||
/** | ||
* Builds the Octokit and graphql instances to be used across the app | ||
* @param {Octokit & Constructor} OctokitExtra | ||
* @param {GitHubConfig} gitHubConfig | ||
* @returns {{octokit: Octokit, graphql: graphql}} | ||
*/ | ||
function octokitFactory(OctokitExtra, gitHubConfig) { | ||
const baseUrl = gitHubConfig.baseUrl | ||
const auth = { | ||
appId: gitHubConfig.app.id, | ||
privateKey: Buffer.from(gitHubConfig.app.privateKey, 'base64').toString( | ||
'utf8' | ||
), | ||
installationId: gitHubConfig.app.installationId | ||
} | ||
|
||
const octokit = new OctokitExtra( | ||
removeNil({ | ||
authStrategy: createAppAuth, | ||
auth, | ||
request: { fetch: proxyFetch, baseUrl }, | ||
baseUrl | ||
}) | ||
) | ||
|
||
const graphql = octokit.graphql.defaults( | ||
removeNil({ | ||
request: { | ||
hook: octokit.auth.hook, | ||
fetch: proxyFetch | ||
}, | ||
baseUrl | ||
}) | ||
) | ||
|
||
return { octokit, graphql } | ||
} | ||
|
||
export { octokitFactory } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { createAppAuth } from '@octokit/auth-app' | ||
import { octokitFactory } from '~/src/helpers/oktokit/factory.js' | ||
import { proxyFetch } from '~/src/helpers/proxy/proxy-fetch.js' | ||
|
||
const buildConfig = (baseUrl) => ({ | ||
...(baseUrl && { baseUrl }), | ||
isStubbed: baseUrl !== undefined, | ||
app: { | ||
id: '123', | ||
privateKey: 'bW9jay1wcml2YXRlLWtleQ==', // base64 encoded 'mock-private-key' string | ||
installationId: '456' | ||
} | ||
}) | ||
|
||
describe('#octokitFactory', () => { | ||
const mockGraphqlDefaults = jest.fn() | ||
const mockOctokitExtra = jest.fn().mockReturnValue({ | ||
graphql: { defaults: mockGraphqlDefaults }, | ||
auth: { hook: 'mockAuthHook' } | ||
}) | ||
const stubBaseUrl = 'http://cdp.127.0.0.1.sslip.io:3333' | ||
|
||
describe('When stubbed', () => { | ||
beforeEach(() => { | ||
octokitFactory(mockOctokitExtra, buildConfig(stubBaseUrl)) | ||
}) | ||
|
||
test('Should call OctokitExtra for a stubbed octokit instance', () => { | ||
expect(mockOctokitExtra).toHaveBeenCalledWith({ | ||
authStrategy: createAppAuth, | ||
auth: { | ||
appId: '123', | ||
privateKey: 'mock-private-key', | ||
installationId: '456' | ||
}, | ||
request: { | ||
fetch: proxyFetch, | ||
baseUrl: stubBaseUrl | ||
}, | ||
baseUrl: stubBaseUrl | ||
}) | ||
}) | ||
|
||
test('Should call OctokitExtra for a stubbed graphql instance', () => { | ||
expect(mockGraphqlDefaults).toHaveBeenCalledWith({ | ||
request: { | ||
hook: 'mockAuthHook', | ||
fetch: proxyFetch | ||
}, | ||
baseUrl: stubBaseUrl | ||
}) | ||
}) | ||
}) | ||
|
||
describe('When not stubbed', () => { | ||
beforeEach(() => { | ||
octokitFactory(mockOctokitExtra, buildConfig()) | ||
}) | ||
|
||
test('Should call OctokitExtra for a non stubbed octokit instance', () => { | ||
expect(mockOctokitExtra).toHaveBeenCalledWith({ | ||
authStrategy: createAppAuth, | ||
auth: { | ||
appId: '123', | ||
privateKey: 'mock-private-key', | ||
installationId: '456' | ||
}, | ||
request: { | ||
fetch: proxyFetch | ||
} | ||
}) | ||
}) | ||
|
||
test('Octokit extra options should not include baseUrl', () => { | ||
expect(mockOctokitExtra.mock.calls[0][0]).not.toHaveProperty('baseUrl') | ||
expect(mockOctokitExtra.mock.calls[0][0].request).not.toHaveProperty( | ||
'baseUrl' | ||
) | ||
}) | ||
|
||
test('Should call OctokitExtra for a non stubbed graphql instance', () => { | ||
expect(mockGraphqlDefaults).toHaveBeenCalledWith({ | ||
request: { | ||
hook: 'mockAuthHook', | ||
fetch: proxyFetch | ||
} | ||
}) | ||
}) | ||
|
||
test('Octokit graphql defaults should not include baseUrl', () => { | ||
expect(mockGraphqlDefaults.mock.calls[0][0]).not.toHaveProperty('baseUrl') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Octokit } from '@octokit/core' | ||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods' | ||
import { createPullRequest } from 'octokit-plugin-create-pull-request' | ||
|
||
import { config } from '~/src/config/index.js' | ||
import { octokitFactory } from '~/src/helpers/oktokit/factory.js' | ||
|
||
const OctokitExtra = Octokit.plugin(restEndpointMethods, createPullRequest) | ||
const githubConfig = config.get('github') | ||
|
||
const { octokit, graphql } = octokitFactory(OctokitExtra, githubConfig) | ||
|
||
export { octokit, graphql } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import isNil from 'lodash/isNil' | ||
|
||
/** | ||
* Remove null and undefined values from an object | ||
* @param {object} obj | ||
* @returns {object} | ||
*/ | ||
function removeNil(obj) { | ||
if (Array.isArray(obj)) { | ||
return obj.map(removeNil).filter((value) => !isNil(value)) | ||
} else if (!isNil(obj) && typeof obj === 'object') { | ||
return Object.entries(obj).reduce((cleaned, [key, value]) => { | ||
const clean = removeNil(value) | ||
if (!isNil(clean)) { | ||
cleaned[key] = clean | ||
} | ||
return cleaned | ||
}, {}) | ||
} | ||
return obj | ||
} | ||
|
||
export { removeNil } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { removeNil } from '~/src/helpers/remove-nill.js' | ||
|
||
describe('#removeNil', () => { | ||
test('Should remove null and undefined values from a flat object', () => { | ||
const input = { a: 1, b: null, c: undefined, d: 4 } | ||
const expected = { a: 1, d: 4 } | ||
|
||
expect(removeNil(input)).toEqual(expected) | ||
}) | ||
|
||
test('Should remove null and undefined values from a nested object', () => { | ||
const input = { a: 1, b: { c: null, d: 4, e: undefined }, f: 5 } | ||
const expected = { a: 1, b: { d: 4 }, f: 5 } | ||
|
||
expect(removeNil(input)).toEqual(expected) | ||
}) | ||
|
||
test('Should remove null and undefined values from an array', () => { | ||
const input = [1, null, 2, undefined, 3] | ||
const expected = [1, 2, 3] | ||
|
||
expect(removeNil(input)).toEqual(expected) | ||
}) | ||
|
||
test('Should remove null and undefined values from a nested array', () => { | ||
const input = [1, [null, 2, undefined], 3] | ||
const expected = [1, [2], 3] | ||
|
||
expect(removeNil(input)).toEqual(expected) | ||
}) | ||
|
||
test('Should return the same value for non-object and non-array inputs', () => { | ||
expect(removeNil(1)).toBe(1) | ||
expect(removeNil(null)).toBeNull() | ||
expect(removeNil(undefined)).toBeUndefined() | ||
expect(removeNil('string')).toBe('string') | ||
}) | ||
|
||
test('Should handle complex nested structures', () => { | ||
const input = { a: [1, null, { b: undefined, c: 3 }], d: { e: [null, 4] } } | ||
const expected = { a: [1, { c: 3 }], d: { e: [4] } } | ||
|
||
expect(removeNil(input)).toEqual(expected) | ||
}) | ||
}) |