Merge pull request #8596 from cmpadden/adr013-adherance

Replace `got` and `axios` dependencies with `node-fetch`
This commit is contained in:
Patrik Oldsberg
2021-12-27 17:05:51 +01:00
committed by GitHub
9 changed files with 240 additions and 74 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend': patch
---
Replaces the usage of `got` with `node-fetch` in the `getUserPhoto` method of the Microsoft provider
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-rollbar-backend': patch
---
Replace the usage of `axios` with `node-fetch` in the Rollbar API
-1
View File
@@ -46,7 +46,6 @@
"express-promise-router": "^4.1.0",
"express-session": "^1.17.1",
"fs-extra": "9.1.0",
"got": "^11.5.2",
"helmet": "^4.0.0",
"jose": "^1.27.1",
"jwt-decode": "^3.1.0",
@@ -20,6 +20,9 @@ import { OAuthResult } from '../../lib/oauth';
import { getVoidLogger } from '@backstage/backend-common';
import { TokenIssuer } from '../../identity/types';
import { CatalogIdentityClient } from '../../lib/catalog';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const mockFrameHandler = jest.spyOn(
helpers,
@@ -28,8 +31,62 @@ const mockFrameHandler = jest.spyOn(
() => Promise<{ result: OAuthResult; privateInfo: any }>
>;
const mockResult = {
result: {
fullProfile: {
emails: [
{
type: 'work',
value: 'conrad@example.com',
},
],
displayName: 'Conrad',
name: {
familyName: 'Ribas',
givenName: 'Francisco',
},
id: 'conrad',
provider: 'microsoft',
photos: [
{
value: 'some-data',
},
],
},
params: {
id_token: 'idToken',
scope: 'scope',
expires_in: 123,
},
accessToken: 'accessToken',
},
privateInfo: {
refreshToken: 'wacka',
},
};
const server = setupServer();
setupRequestMockHandlers(server);
const setupHandlers = () => {
server.use(
rest.get(
'https://graph.microsoft.com/v1.0/me/photos/*',
async (_, res, ctx) => {
const imageBuffer = new Uint8Array([104, 111, 119, 100, 121]).buffer;
return res(
ctx.set('Content-Length', imageBuffer.byteLength.toString()),
ctx.set('Content-Type', 'image/jpeg'),
ctx.body(imageBuffer),
);
},
),
);
};
describe('createMicrosoftProvider', () => {
it('should auth', async () => {
setupHandlers();
const tokenIssuer = {
issueToken: jest.fn(),
listPublicKeys: jest.fn(),
@@ -55,39 +112,7 @@ describe('createMicrosoftProvider', () => {
callbackUrl: 'mock',
});
mockFrameHandler.mockResolvedValueOnce({
result: {
fullProfile: {
emails: [
{
type: 'work',
value: 'conrad@example.com',
},
],
displayName: 'Conrad',
name: {
familyName: 'Ribas',
givenName: 'Francisco',
},
id: 'conrad',
provider: 'microsoft',
photos: [
{
value: 'some-data',
},
],
},
params: {
id_token: 'idToken',
scope: 'scope',
expires_in: 123,
},
accessToken: 'accessToken',
},
privateInfo: {
refreshToken: 'wacka',
},
});
mockFrameHandler.mockResolvedValueOnce(mockResult);
const { response } = await provider.handler({} as any);
expect(response).toEqual({
providerInfo: {
@@ -103,4 +128,45 @@ describe('createMicrosoftProvider', () => {
},
});
});
it('should return the base64 encoded photo data of the profile', async () => {
setupHandlers();
const tokenIssuer = {
issueToken: jest.fn(),
listPublicKeys: jest.fn(),
};
const catalogIdentityClient = {
findUser: jest.fn(),
};
const provider = new MicrosoftAuthProvider({
logger: getVoidLogger(),
catalogIdentityClient:
catalogIdentityClient as unknown as CatalogIdentityClient,
tokenIssuer: tokenIssuer as unknown as TokenIssuer,
authHandler: async ({ fullProfile }) => ({
profile: {
email: fullProfile.emails![0]!.value,
displayName: fullProfile.displayName,
picture: 'http://microsoft.com/lols',
},
}),
clientId: 'mock',
clientSecret: 'mock',
callbackUrl: 'mock',
// define resolver to return user `info` for photo validation
signInResolver: async (info, _) => {
return {
id: 'user.name',
token: 'token',
info: info,
};
},
});
mockFrameHandler.mockResolvedValueOnce(mockResult);
const { response } = await provider.handler({} as any);
const overloadedIdentity = response.backstageIdentity as any;
const photo = overloadedIdentity.info.result.fullProfile.photos[0];
expect(photo.value).toEqual('data:image/jpeg;base64,aG93ZHk=');
});
});
@@ -45,7 +45,7 @@ import {
SignInResolver,
} from '../types';
import { Logger } from 'winston';
import got from 'got';
import fetch from 'node-fetch';
type PrivateInfo = {
refreshToken: string;
@@ -173,19 +173,17 @@ export class MicrosoftAuthProvider implements OAuthHandlers {
private getUserPhoto(accessToken: string): Promise<string | undefined> {
return new Promise(resolve => {
got
.get('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', {
encoding: 'binary',
responseType: 'buffer',
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then(photoData => {
const photoURL = `data:image/jpeg;base64,${Buffer.from(
photoData.body,
fetch('https://graph.microsoft.com/v1.0/me/photos/48x48/$value', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
})
.then(response => response.arrayBuffer())
.then(arrayBuffer => {
const imageUrl = `data:image/jpeg;base64,${Buffer.from(
arrayBuffer,
).toString('base64')}`;
resolve(photoURL);
resolve(imageUrl);
})
.catch(error => {
this.logger.warn(
+3 -1
View File
@@ -33,8 +33,8 @@
"dependencies": {
"@backstage/backend-common": "^0.10.0",
"@backstage/config": "^0.1.10",
"@backstage/test-utils": "^0.1.24",
"@types/express": "^4.17.6",
"axios": "^0.24.0",
"camelcase-keys": "^6.2.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
@@ -44,12 +44,14 @@
"helmet": "^4.0.0",
"lodash": "^4.17.21",
"morgan": "^1.10.0",
"node-fetch": "^2.6.1",
"winston": "^3.2.1",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.3",
"@types/supertest": "^2.0.8",
"msw": "^0.36.3",
"supertest": "^6.1.3"
},
"files": [
@@ -14,7 +14,12 @@
* limitations under the License.
*/
import { getRequestHeaders } from './RollbarApi';
import { getRequestHeaders, RollbarApi } from './RollbarApi';
import { setupRequestMockHandlers } from '@backstage/test-utils';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { getVoidLogger } from '@backstage/backend-common';
import { RollbarProject } from './types';
describe('RollbarApi', () => {
describe('getRequestHeaders', () => {
@@ -26,4 +31,31 @@ describe('RollbarApi', () => {
});
});
});
describe('getAllProjects', () => {
const server = setupServer();
setupRequestMockHandlers(server);
const mockBaseUrl = 'https://api.rollbar.com/api/1';
const mockProjects: RollbarProject[] = [
{ id: 123, name: 'abc', accountId: 1, status: 'enabled' },
{ id: 456, name: 'xyz', accountId: 1, status: 'enabled' },
];
const setupHandlers = () => {
server.use(
rest.get(`${mockBaseUrl}/projects`, (_, res, ctx) => {
return res(ctx.json({ result: mockProjects }));
}),
);
};
it('should return all projects with a name attribute', async () => {
setupHandlers();
const api = new RollbarApi('my-access-token', getVoidLogger());
const projects = await api.getAllProjects();
expect(projects).toEqual(mockProjects);
});
});
});
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import axios from 'axios';
import { Logger } from 'winston';
import camelcaseKeys from 'camelcase-keys';
import { buildQuery } from '../util';
@@ -25,6 +24,7 @@ import {
RollbarProjectAccessToken,
RollbarTopActiveItem,
} from './types';
import fetch from 'node-fetch';
const baseUrl = 'https://api.rollbar.com/api/1';
@@ -110,11 +110,12 @@ export class RollbarApi {
this.logger.info(`Calling Rollbar REST API, ${fullUrl}`);
}
return axios
.get(fullUrl, getRequestHeaders(accessToken || this.accessToken || ''))
.then(response =>
camelcaseKeys<T>(response?.data?.result, { deep: true }),
);
return fetch(
fullUrl,
getRequestHeaders(accessToken || this.accessToken || ''),
)
.then(response => response.json())
.then(json => camelcaseKeys<T>(json?.result, { deep: true }));
}
private async getForProject<T>(
+77 -19
View File
@@ -4963,7 +4963,7 @@
"@types/set-cookie-parser" "^2.4.0"
set-cookie-parser "^2.4.6"
"@mswjs/interceptors@^0.12.6":
"@mswjs/interceptors@^0.12.6", "@mswjs/interceptors@^0.12.7":
version "0.12.7"
resolved "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.12.7.tgz#0d1cd4cd31a0f663e0455993951201faa09d0909"
integrity sha512-eGjZ3JRAt0Fzi5FgXiV/P3bJGj0NqsN7vBS0J0FO2AQRQ0jCKQS4lEFm4wvlSgKQNfeuc/Vz6d81VtU3Gkx/zg==
@@ -10032,13 +10032,6 @@ axios@^0.21.1, axios@^0.21.4:
dependencies:
follow-redirects "^1.14.0"
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
integrity sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==
dependencies:
follow-redirects "^1.14.4"
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@@ -11145,6 +11138,14 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@4.1.1, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^1.0.0, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -11164,14 +11165,6 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
change-case-all@1.0.14:
version "1.0.14"
resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1"
@@ -15284,7 +15277,7 @@ fn.name@1.x.x:
resolved "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==
follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.14.4:
follow-redirects@^1.0.0, follow-redirects@^1.14.0:
version "1.14.6"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd"
integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==
@@ -16132,7 +16125,7 @@ google-p12-pem@^3.0.3:
dependencies:
node-forge "^0.10.0"
got@^11.5.2, got@^11.8.0, got@^11.8.2:
got@^11.8.0, got@^11.8.2:
version "11.8.2"
resolved "https://registry.npmjs.org/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599"
integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==
@@ -17278,7 +17271,7 @@ inquirer@^8.0.0:
strip-ansi "^6.0.0"
through "^2.3.6"
inquirer@^8.1.1:
inquirer@^8.1.1, inquirer@^8.2.0:
version "8.2.0"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz#f44f008dd344bbfc4b30031f45d984e034a3ac3a"
integrity sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==
@@ -21419,6 +21412,32 @@ msw@^0.35.0:
type-fest "^1.2.2"
yargs "^17.0.1"
msw@^0.36.3:
version "0.36.3"
resolved "https://registry.npmjs.org/msw/-/msw-0.36.3.tgz#7feb243a5fcf563806d45edc027bc36144741170"
integrity sha512-Itzp/QhKaleZoslXDrNik3ramW9ynqzOdbwydX2ehBSSaZd5QoiAl/bHYcV33R6CEZcJgIX1N4s+G6XkF/bhkA==
dependencies:
"@mswjs/cookies" "^0.1.6"
"@mswjs/interceptors" "^0.12.7"
"@open-draft/until" "^1.0.3"
"@types/cookie" "^0.4.1"
"@types/inquirer" "^8.1.3"
"@types/js-levenshtein" "^1.1.0"
chalk "4.1.1"
chokidar "^3.4.2"
cookie "^0.4.1"
graphql "^15.5.1"
headers-utils "^3.0.2"
inquirer "^8.2.0"
is-node-process "^1.0.1"
js-levenshtein "^1.1.6"
node-fetch "^2.6.1"
path-to-regexp "^6.2.0"
statuses "^2.0.0"
strict-event-emitter "^0.2.0"
type-fest "^1.2.2"
yargs "^17.3.0"
multicast-dns-service-types@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
@@ -23033,6 +23052,11 @@ path-to-regexp@^1.7.0:
dependencies:
isarray "0.0.1"
path-to-regexp@^6.2.0:
version "6.2.0"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -27005,6 +27029,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string.prototype.matchall@^4.0.0 || ^3.0.1", string.prototype.matchall@^4.0.6:
version "4.0.6"
resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa"
@@ -27102,6 +27135,13 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.0:
version "7.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
@@ -29949,6 +29989,11 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^21.0.0:
version "21.0.0"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55"
integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==
yargs-parser@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-3.2.0.tgz#5081355d19d9d0c8c5d81ada908cb4e6d186664f"
@@ -30013,6 +30058,19 @@ yargs@^17.1.1:
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs@^17.3.0:
version "17.3.0"
resolved "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz#295c4ffd0eef148ef3e48f7a2e0f58d0e4f26b1c"
integrity sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.0.0"
yargs@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e"