Merge pull request #8596 from cmpadden/adr013-adherance
Replace `got` and `axios` dependencies with `node-fetch`
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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>(
|
||||
|
||||
Reference in New Issue
Block a user