Problem: Consumer Driven Contract Testing with Pact - javascript

Im writing a small programm for a University project.
I'd like to test it with the pact framewok. Unfortunately, no Pact.json file is created for me, although there are no errors. My Consumer iss written in Javascript.
Below you can see the source code of my javascript file, the console output and my package.json file:
const {Pact} = require('#pact-foundation/pact');
const axios = require('axios');
const path = require('path');
describe('Pact Consumer', () => {
const provider = new Pact({
consumer: 'consumer',
provider: 'producer',
host:'127.0.0.1',
port: 1234,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: 'INFO',
});
beforeAll(() => provider.setup());
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
describe('consumer', () => {
beforeEach
(() =>
provider.addInteraction({
state: "valid date",
uponReceiving: "a request for JSON data",
withRequest: {
method: "GET",
path: "/test",
headers: { Accept: "application/json" },
},
willRespondWith: {
status: 200,
headers: { "Content-Type": "application/json" },
body:
{
name: 'Scherr',
surname: 'Valerian',
age: 28,
},
},
}),
);
});
describe('test', () => {
it('should return the correct data', () => {
return axios.get('localhost:1234/test').then(response => {
expect(response[0].name).toBe('Scherr');
expect(response[0].surname).toBe('Valerian');
expect(response[0].age).toBe(28);
}).then(()=> provider.verify())
});
});
afterAll(() => {
return provider.finalize();
});
});
Console Output:
Error: getaddrinfo ENOTFOUND 1234
Expected :
Actual :
<Click to see difference>
error properties: Object({ errno: 'ENOTFOUND', code: 'ENOTFOUND', syscall: 'getaddrinfo', hostname: '1234', config: Object({ url: 'localhost:1234/test', method: 'get', headers: Object({ Accept: 'application/json, text/plain, */*', User-Agent: 'axios/0.19.2' }), transformRequest: [ Function ], transformResponse: [ Function ], timeout: 0, adapter: Function, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, validateStatus: Function, data: undefined }), request: Writable({ _writableState: WritableState({ objectMode: false, highWaterMark: 16384, finalCalled: false, needDrain: false, ending: false, ended: false, finished: false, destroyed: false, decodeStrings: true, defaultEncoding: 'utf8', length: 0, writing: false, corked: 0, sync: true, bufferProcessing: false, onwrite: Function, writecb: null, writelen: 0, afterWriteTickInfo: null, bufferedRequest: null, lastBufferedRequest: null, pendingcb: 0, prefinished: false, errorEmitted: false, emitClose: true, autoDestroy: false ...
Error: getaddrinfo ENOTFOUND 1234
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:64:26)
package.json:
{
"name": "webservice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "jasmine"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"#pact-foundation/pact": "^9.11.0",
"#pact-foundation/pact-node": "^10.9.5",
"axios": "^0.19.2",
"jasmine": "^3.5.0"
},
"devDependencies": {
"pact": "^4.3.2"
}
}
I am grateful for any help and thanks in advance

I can't tell without logs here, but one things is for sure: your call to axios.get and provider.verify are promises and are not being correctly, meaning the execution of certain things will be out of order or will not actually execute at all.
Simply prefixing both with return should fix that issue.
See https://github.com/pact-foundation/pact-js#test-fails-when-it-should-pass.

I had a similar issue. Setting the cors property on the provider config to true did the job for me.
I didn't seem to have this problem when using node-fetch as opposed to axios.

Related

How to add bearer token in the API called in getServerSideProps() in nextjs?

Intro
I am developing an login application using Nextjs on frontend and springboot on backend.
Problem
I am able to login from the frontend which calls the loginAPI named /authenticate developed in springboot and it is successfully returning the authToken.After login I got redirects to home (/allcoupons) page.
In login , I am passing the value of token as response.data like this
const handleSubmit = async (e) => {
// e.preventDefault();
let requestbody = {
username: credentials.username,
password: credentials.password,
};
try {
const response = await axios({
method: "post",
headers: {},
url: "http://localhost:8081/authenticate",
data: requestbody,
});
//console.log("credentials for login = ", credentials);
//console.log("response from api = ", response);
if (response.status === 200) {
router.push(
{
pathname: "/allcoupons",
query: { auth: JSON.stringify(response.data) },
},
undefined,
{
shallow: true
}
);
} else alert("invalid credentials");
} catch (error) {
setTimeout(() => {
setShowAlert(true);
}, 2000);
setShowAlert(false);
}
};
Then I redirects to /allcoupons component
In this component I am using getServerSideProps(). I am able to access the authtoken value from context.query.auth but now I am unable to send this authtoken value as bearer token with another API /allcoupons in the headers.
home.js
import React, { Fragment, useState, useEffect } from "react";
import { useRouter } from "next/router";
import Footer from "./components/footer";
import { Alert } from "flowbite-react";
import axios from "axios";
const Allcoupons = ({ datafromAPI }) => {
const router = useRouter();
//console.log("datafromAPI in components = ", datafromAPI.coupons)
//let data = datafromAPI.coupons;
const val = router.query.auth ? JSON.parse(router.query.auth) : {};
console.log("authtoken form login = ", val);
const [showModal, setShowModal] = useState(false);
.
.
return (
<>
.
.
.
</>
);
};
export async function getServerSideProps(context) {
//Fetch data from get API
//console.log("context = ", context.query.auth);
const token = context.query.auth;
console.log("token = ",context.query.auth)
const res = await axios.get(
`http://localhost:8081/couponstore/v1.0/coupons`,
//using like this
{ headers: { Authorization: `Bearer ${token}` } }
);
const datafromAPI = res.data;
console.log("data from API server = ",datafromAPI);
return { props: { datafromAPI } };
}
Observation
But as soon as I redirects to this page , it endpoint change from /allcoupons to /allcoupons?auth={authtokenvalue} .
Logs/StackTrace
Also this log comes in the terminal
token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"
error - [AxiosError: Request failed with status code 403] {
code: 'ERR_BAD_REQUEST',
config: {
transitional: {
silentJSONParsing: true,
forcedJSONParsing: true,
clarifyTimeoutError: false
},
adapter: [Function: httpAdapter],
transformRequest: [ [Function: transformRequest] ],
transformResponse: [ [Function: transformResponse] ],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: [Function] },
validateStatus: [Function: validateStatus],
headers: {
Accept: 'application/json, text/plain, */*',
Authorization: 'Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"',
'User-Agent': 'axios/0.27.2'
},
method: 'get',
url: 'http://localhost:8081/couponstore/v1.0/coupons',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype] {
abort: [Function (anonymous)],
aborted: [Function (anonymous)],
connect: [Function (anonymous)],
error: [Function (anonymous)],
socket: [Function (anonymous)],
timeout: [Function (anonymous)],
prefinish: [Function: requestOnPrefinish]
},
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'localhost',
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
_writableState: [WritableState],
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
parser: null,
_httpMessage: [Circular *1],
write: [Function: writeAfterFIN],
[Symbol(async_id_symbol)]: 285969,
[Symbol(kHandle)]: [TCP],
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: true,
[Symbol(kSetKeepAliveInitialDelay)]: 60,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0,
[Symbol(RequestTimeout)]: undefined
},
_header: 'GET /couponstore/v1.0/coupons HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost:8081\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: Agent {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: undefined,
defaultPort: 80,
protocol: 'http:',
options: [Object: null prototype],
requests: [Object: null prototype] {},
sockets: [Object: null prototype],
freeSockets: [Object: null prototype] {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
scheduling: 'lifo',
maxTotalSockets: Infinity,
totalSocketCount: 1,
[Symbol(kCapture)]: false
},
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/couponstore/v1.0/coupons',
_ended: true,
res: IncomingMessage {
_readableState: [ReadableState],
_events: [Object: null prototype],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Socket],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
rawHeaders: [Array],
rawTrailers: [],
aborted: false,
upgrade: false,
url: '',
method: null,
statusCode: 403,
statusMessage: '',
client: [Socket],
_consuming: false,
_dumped: false,
req: [Circular *1],
responseUrl: 'http://localhost:8081/couponstore/v1.0/coupons',
redirects: [],
[Symbol(kCapture)]: false,
[Symbol(kHeaders)]: [Object],
[Symbol(kHeadersCount)]: 18,
[Symbol(kTrailers)]: null,
[Symbol(kTrailersCount)]: 0,
[Symbol(RequestTimeout)]: undefined
},
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: Writable {
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 3,
_maxListeners: undefined,
_options: [Object],
_ended: true,
_ending: true,
_redirectCount: 0,
_redirects: [],
_requestBodyLength: 0,
_requestBodyBuffers: [],
_onNativeResponse: [Function (anonymous)],
_currentRequest: [Circular *1],
_currentUrl: 'http://localhost:8081/couponstore/v1.0/coupons',
[Symbol(kCapture)]: false
},
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype] {
accept: [Array],
authorization: [Array],
'user-agent': [Array],
host: [Array]
},
[Symbol(kUniqueHeaders)]: null
},
response: {
status: 403,
statusText: '',
headers: {
'x-content-type-options': 'nosniff',
'x-xss-protection': '1; mode=block',
'cache-control': 'no-cache, no-store, max-age=0, must-revalidate',
pragma: 'no-cache',
expires: '0',
'x-frame-options': 'DENY',
'content-length': '0',
date: 'Fri, 30 Sep 2022 10:27:40 GMT',
connection: 'close'
},
config: {
transitional: [Object],
adapter: [Function: httpAdapter],
transformRequest: [Array],
transformResponse: [Array],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
maxBodyLength: -1,
env: [Object],
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'get',
url: 'http://localhost:8081/couponstore/v1.0/coupons',
data: undefined
},
request: <ref *1> ClientRequest {
_events: [Object: null prototype],
_eventsCount: 7,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
destroyed: false,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
maxRequestsOnConnectionReached: false,
_defaultKeepAlive: true,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
_closed: false,
socket: [Socket],
_header: 'GET /couponstore/v1.0/coupons HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'Authorization: Bearer "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcnVuYWJoIiwiZXhwIjoxNjY0NTQwODYwLCJpYXQiOjE2NjQ1MzM2NjB9.Ib3X-6TMyQ3YfoGz-PuS4hehzuHq-N6XHwzLF6cXD2U"\r\n' +
'User-Agent: axios/0.27.2\r\n' +
'Host: localhost:8081\r\n' +
'Connection: close\r\n' +
'\r\n',
_keepAliveTimeout: 0,
_onPendingData: [Function: nop],
agent: [Agent],
socketPath: undefined,
method: 'GET',
maxHeaderSize: undefined,
insecureHTTPParser: undefined,
path: '/couponstore/v1.0/coupons',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
host: 'localhost',
protocol: 'http:',
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype],
[Symbol(kUniqueHeaders)]: null
},
data: ''
},
page: '/allcoupons'
}
Whats the mistake here??
This how do usually when I create react apps
I create a global context
import { createContext } from "react";
import { GlobalContextType } from "../types";
const defaultValue: GlobalContextType = {
dispatch: () => {},
state: {
user: { authToken: "", username: "" }
},
};
export const GlobalContext = createContext(defaultValue);
create a hook for my global context
import { useContext } from "react";
import { GlobalContext } from "../contexts";
export function useGlobalContext () {
const context = useContext(GlobalContext)
if (!context) throw new Error("Missing Context - Global context");
return context;
}
Create state reducer hook
import { Dispatch, useMemo, useReducer } from "react";
import {
GlobalContextActions,
GlobalContextState,
} from "../types";
export function globalContextReducer(
state: GlobalContextState,
action: ReducerAction<GlobalContextActions>
): GlobalContextState {
switch (action.type) {
case GlobalContextActions.setUser:
return {
...state,
user: action.payload
};
default:
return state;
}
}
export function useGlobalContextReducer(reducer = globalContextReducer) {
const [state, dispatch] = useReducer(reducer, {
user : {authToken: "", username: ""}
});
return useMemo(() => {
return {
dispatch,
state,
};
}, [d, state]);
}
Wrapped the whole App with my global context
import { GlobalContext } from "../src/contexts";
import { useGlobalContextReducer } from "../src/hooks/use-global-context-reducer";
export default function MyApp(props: MyAppProps) {
const { Component pageProps } = props;
const globalContextReducer = useGlobalContextReducer();
return (
<GlobalContext.Provider value={globalContextReducer}>
<Component {...pageProps} />
</GlobalContext.Provider>
);
}
this just my setup
then in my login page
import {GlobalContextActions, useGlobalContext} from "../../context"
import {Routes} from "../routes"
function Login() {
const { dispatch} = useGlobalContext();
const router = useRouter();
const [form, updateForm] = useState({
email: "",
password: "",
});
const onFieldChange = (event: ChangeEvent<HTMLInputElement>) => {
const { target } = event;
const { id, value } = target;
updateForm((current) => ({ ...current, [id]: value }));
};
const isLoginDisabled = !validateEmail(form.email) || !form.password;
const onLogin = async () => {
const dataService = container.resolve(DataService);
const result = await dataService.getLoginData(form.email, form.password);
if (result.error) {
return dispatchError(result?.errorMessage as string);
}
useGlobalContextReducer({action:GlobalContextActions.setUser, payload: result.data});
router.push(Routes.home);
};
return (
<Box>
<form action="" onSubmit={(event) => event?.preventDefault()}>
<TextField
id="email"
label={"email"}
variant="standard"
required
value={form.email}
onChange={onFieldChange}
type="email"
fullWidth
data-testid="email"
/>
<TextField
id="password"
label={"password"}
variant="standard"
type="password"
value={form.password}
required
fullWidth
onChange={onFieldChange}
data-testid="password"
/>
<Button type="submit" variant="contained" disabled={isLoginDisabled} onClick={onLogin}>
Login
</Button>
</form>
</Box>
);
}
export default Login;
and this how I use the global state
function MyPage() {
const {state} = useGlobalContext
return (<div>{JSON.stringinfy(state)}</div>)
}
First of all I do not recommend returning authtoken in response of successful login. You should use httpOnly cookies to set authtoken. It is more secure and you can set it from backend like this.
Second axios provide a way to set Authorization token in request. I mean you can alter default headers like below:
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
Read more in doc

HERE API: 'Illegal API Key Id'

I took on the role of a maintainer in an app that has this code, which uses the HERE API:
{
name: 'onEnderecoSave',
listenModel: 'ClienteEndereco',
methodsHook: ['afterSave'],
async handle (payload) {
const mapMethods = {
afterSave: async () => {
const { entity: enderecoSaved, options, models } = payload
const { ConfiguracoesGerais } = models
const { transaction } = options
try {
const cepResult = await utilitarios.buscarCep(enderecoSaved.cep)
if (cepResult.erro) {
throw new CustomError('CEP inválido! Erro ao tentar consumir os dados deste cep')
}
const { bairro, localidade, logradouro, uf } = cepResult
const enderecoBaseCep = `${logradouro} - ${bairro}, ${localidade} - ${uf}`
const configuracoesGerais = await ConfiguracoesGerais.findOne({
where: {}
})
if (!configuracoesGerais) {
throw new CustomError('Ocorreu um problema para identificar as configurações gerais do sistema')
}
const execGeoCode = (cepResult, mixinParams = {}) => {
const geoCodeParams = queryString.stringify({
country: 'Brazil',
state: cepResult.uf,
city: cepResult.localidade,
district: cepResult.uf,
postalCode: String(enderecoSaved.cep).replace(/[^\d]+/g, ''),
...(cepResult.logradouro ? { street: cepResult.logradouro } : {})
}, ';')
return request.get(`https://geocode.search.hereapi.com/v1/geocode?apiKey=${configuracoesGerais.hereApiKey}&qq=${geoCodeParams}`)
}
const { latitude, longitude } = await execGeoCode(cepResult)
.then(async ({ data: hereResult }) => {
if (!hereResult.items.length) {
const { data } = await execGeoCode({
...cepResult,
logradouro: cepResult.bairro
})
hereResult = data
if (!hereResult.items.length) {
const { data } = await execGeoCode({
...cepResult,
logradouro: undefined
})
hereResult = data
}
}
const latitude = lodash.get(hereResult, 'items["0"].position.lat')
const longitude = lodash.get(hereResult, 'items["0"].position.lng')
return { latitude, longitude }
})
if (!latitude || !longitude) {
throw new CustomError('CEP inválido! Erro ao tentar consumir os dados deste cep')
}
const { x, y } = { y: configuracoesGerais.latitudeRestaurante, x: configuracoesGerais.longitudeRestaurante }
const { data: resultRouter } = await request.get(`https://router.hereapi.com/v8/routes?destination=${latitude},${longitude}&origin=${y},${x}&return=summary&transportMode=car&apiKey=${configuracoesGerais.hereApiKey}`)
if (!resultRouter) {
throw new CustomError('CEP inválido! Erro ao tentar verificar rotas de entrega')
}
let distanceKm = lodash.get(resultRouter, 'routes[0].sections[0].summary.length', null)
if (distanceKm === null) {
throw new CustomError('CEP inválido! Erro ao tentar verificar rotas de entrega')
}
distanceKm = distanceKm / 1000 // o Here informa a distância em metros
enderecoSaved.enderecoBaseCep = enderecoBaseCep
enderecoSaved.$noCyclic = true
await enderecoSaved.update({
nomeBairroDistrito: bairro,
enderecoBaseCep,
taxaEntrega: 0,
distanciaDoRestauranteKm: distanceKm
}, {
hooks: false,
transaction
})
} catch (error) {
console.error('teste', error)
if (enderecoSaved) {
await enderecoSaved.destroy()
}
throw error
}
}
}
return mapMethods[payload.methodHook] ? mapMethods[payload.methodHook]() : null
}
And, when i make a request on the app, i get this error in return:
response: {
status: 401,
statusText: 'Unauthorized',
headers: {
'content-type': 'application/json',
date: 'Sun, 07 Aug 2022 22:50:04 GMT',
server: 'openresty',
'x-request-id': 'REQ-082dbbe9-49a8-4d03-aeeb-2e19b8107057',
'content-length': '66',
connection: 'Close'
},
config: {
adapter: [Function: httpAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: [Function: validateStatus],
headers: [Object],
method: 'get',
url: 'https://router.hereapi.com/v8/routes?destination=-15.79931,-47.86015&origin=-15.8697718,-47.9738754&return=summary&transportMode=car&apiKey=nhtKpzL7jDCdppdqSI2G4sIeQukduxhH74b-6xPcCV8?',
data: undefined
},
request: ClientRequest {
_events: [Object: null prototype],
_eventsCount: 6,
_maxListeners: undefined,
outputData: [],
outputSize: 0,
writable: true,
_last: true,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [TLSSocket],
connection: [TLSSocket],
_header: 'GET /v8/routes?destination=-15.79931,-47.86015&origin=-15.8697718,-47.9738754&return=summary&transportMode=car&apiKey=nhtKpzL7jDCdppdqSI2G4sIeQukduxhH74b-6xPcCV8? HTTP/1.1\r\n' +
'Accept: application/json, text/plain, */*\r\n' +
'User-Agent: axios/0.18.1\r\n' +
'Host: router.hereapi.com\r\n' +
'Connection: close\r\n' +
'\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Agent],
socketPath: undefined,
method: 'GET',
insecureHTTPParser: undefined,
path: '/v8/routes?destination=-15.79931,-47.86015&origin=-15.8697718,-47.9738754&return=summary&transportMode=car&apiKey=nhtKpzL7jDCdppdqSI2G4sIeQukduxhH74b-6xPcCV8?',
_ended: true,
res: [IncomingMessage],
aborted: false,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
reusedSocket: false,
_redirectable: [Writable],
[Symbol(kCapture)]: false,
[Symbol(kNeedDrain)]: false,
[Symbol(corked)]: 0,
[Symbol(kOutHeaders)]: [Object: null prototype]
},
data: { error: 'Unauthorized', error_description: 'Illegal API key id' }
}
}
{ error: 'Unauthorized', error_description: 'Illegal API key id' }
But, the strange thing is that, if i run a simple GET with the same URL on Insomnia:
https://router.hereapi.com/v8/routes?destination=-15.79931,-47.86015&origin=-15.8697718,-47.9738754&return=summary&transportMode=car&apiKey=t1057tOh4Vnx-OYrni_4_mnsTu-BoLVE8ckKYavow1A
It runs with no error:
enter image description here
So, what is the problem here?

How to expect http error and response using chai in nodejs

Hi I am writing a unit test case of my function. this function returns a json response in case request is successful else return an error.
describe("Operations", async () => {
it("user info", async () => {
let accessToken = 'dfsf;
const result = await authConnectors.getUser(accessToken);
});
});
How to assert http error and response using chai in nodejs ?
{ Error: Unauthorized
at Request.callback (/vagrant/node_modules/superagent/lib/node/index.js:699:13)
at IncomingMessage.parser (/vagrant/node_modules/superagent/lib/node/index.js:903:18)
at emitNone (events.js:91:20)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
status: 401,
response:
Response {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
res:
IncomingMessage {
_readableState: [Object],
readable: false,
domain: null,
_events: [Object],
_eventsCount: 4,
_maxListeners: undefined,
socket: [Object],
connection: [Object],
httpVersionMajor: 1,
httpVersionMinor: 1,
httpVersion: '1.1',
complete: true,
headers: [Object],
rawHeaders: [Object],
trailers: {},
rawTrailers: [],
upgrade: false,
url: '',
method: null,
statusCode: 401,
statusMessage: 'Unauthorized',
client: [Object],
_consuming: true,
_dumped: false,
req: [Object],
text: '',
read: [Function] },
request:
Request {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
_agent: false,
_formData: null,
method: 'GET',
url: 'https://id-dev',
_header: [Object],
header: [Object],
writable: true,
_redirects: 0,
_maxRedirects: 5,
cookies: '',
qs: {},
_query: [],
qsRaw: [],
_redirectList: [],
_streamRequest: false,
req: [Object],
protocol: 'https:',
host: 'id-dh',
_endCalled: true,
_callback: [Function],
res: [Object],
response: [Circular],
called: true },
req:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 3,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedHeader: {},
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'GET oqcnM_4xWNbmv-WaoJ63UOjcZSHW-KrULLRUFzXrQKS6iuPWp6poHLjop1WsRtw5Y-KQCTZVI783awnn3_utEw6yqD6qpHHZwhxiPeM8mFZgN_zH4xX0ChbQ3ayao9Ms6ZHQT0X00N467HPfq5hVAW4D3a-3DURwnUvvVTIsYTShhZAsag2P26WDbKapPdgfVpjQKV-4GP--cLWbAZpEWJT3yS31YiY-VEcdfjdDU\r\nAccept: application/json\r\nConnection: close\r\n\r\n',
_headers: [Object],
_headerNames: [Object],
_onPendingData: null,
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/userinfo',
_ended: true,
parser: null,
res: [Object] },
text: '',
body: {},
files: undefined,
buffered: true,
headers:
{ 'content-length': '0',
'strict-transport-security': 'max-age=31536000; includeSubDomains; preload',
'x-xss-protection': '1; mode=block',
'x-content-type-options': 'nosniff',
'content-security-policy': 'default-src \'self\';script-src \'self\' https://track.abc.net;style-src \'self\' \'unsafe-inline\' https://track.abc.net;img-src \'self\' https://track.abc.net;frame-src \'self\' https://track.abc.net;object-src \'none\';',
'www-authenticate': 'Bearer error="invalid_token"',
date: 'Wed, 28 Mar 2018 11:57:56 GMT',
connection: 'close' },
header:
{ 'content-length': '0',
'strict-transport-security': 'max-age=31536000; includeSubDomains; preload',
'x-xss-protection': '1; mode=block',
'x-content-type-options': 'nosniff',
'content-security-policy': 'default-src \'self\';script-src \'self\' https://track.abc.net;style-src \'self\' \'unsafe-inline\' https://track.abc.net;img-src \'self\' https://track.abc.net;frame-src \'self\' https://track.abc.net;object-src \'none\'',
'www-authenticate': 'Bearer error="invalid_token"',
date: 'Wed, 28 Mar 2018 11:57:56 GMT',
connection: 'close' },
statusCode: 401,
status: 401,
statusType: 4,
info: false,
ok: false,
redirect: false,
clientError: true,
serverError: false,
error:
{ Error: cannot GET /sts/connect/userinfo (401)
at Response.toError (/vagrant/node_modules/superagent/lib/node/response.js:94:15)
at ResponseBase._setStatusProperties (/vagrant/node_modules/superagent/lib/response-base.js:123:16)
at new Response (/vagrant/node_modules/superagent/lib/node/response.js:41:8)
at Request._emitResponse (/vagrant/node_modules/superagent/lib/node/index.js:739:20)
at IncomingMessage.parser (/vagrant/node_modules/superagent/lib/node/index.js:903:38)
at emitNone (events.js:91:20)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
status: 401,
text: '',
method: 'GET',
path: '/sts' },
accepted: false,
noContent: false,
badRequest: false,
unauthorized: true,
notAcceptable: false,
forbidden: false,
notFound: false,
type: '',
links: {},
setEncoding: [Function: bound ],
redirects: [] } }
Using expect and should you can test as below:
describe('Testing https responses', () => {
it("user info", async (done) => {
.... request here...
.catch((err) => {
err.response.should.have.status(404);
err.response.body.should.have.property('error');
err.response.body.error.should.eql('User could not be found');
done()
});
})
it('testing using expect', (done) => {
request.get('http://localhost:8000', function (err, res, body) {
expect(res.statusCode).to.equal(400);
expect(res).to.have.property('body');
expect(res.body).to.equal('wrong header');
done();
});
});
})
These tests are asynchronous, so they need the promise to be solved at the end with done()
Edit - Added once more example
.property(name[, val[, msg]])
Asserts that the target has a property with the given key name.
expect({a: 1}).to.have.property('a');
Here three links that can be helpful:
How to properly test for HTTP failure responses
Testing HTTP Responses in Node.js
Michael Herman - Testing Node.js with Mocha and Chai
Chai Doc - method property using expect
You could use try-catch and handle errors =)
it("user info", async () => {
try{
let accessToken = 'dfsf;
const result = await authConnectors.getUser(accessToken);
} catch(e) {
// check error
}
});

YouTube Analytics Goolge Authorization

I am trying to authorise my server for calling the youtube analytics api via the module "googleapis" (https://www.npmjs.com/package/googleapis). I want to use the JWT to authorise. As you can see I get a forbidden error back from the api.
I read a ton of posts and tinkered around for a whole while, but I can't figure out what is wrong.
Is my code ok? Otherwise it as to be a google cloud related issue. (E.g. I did not set up the roles properly there.)
import fs from 'fs';
import { google } from 'googleapis';
// read credentials from file
const credentialsFromFile = fs.readFileSync(`${__dirname}/../credentials.json`);
const credentials = JSON.parse(credentialsFromFile.toString());
const scopes = [
'https://www.googleapis.com/auth/youtube.readonly',
'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
'https://www.googleapis.com/auth/youtubepartner',
];
// get an authorized client
const jwtClient = new google.auth.JWT(
credentials.client_email,
null,
credentials.private_key,
scopes
);
const youtubeAnalyticsClient = google.youtubeAnalytics({
version: 'v1',
auth: jwtClient,
});
function runQuery(callback) {
youtubeAnalyticsClient.reports.query(
{
auth: jwtClient,
ids: 'channel==MINE',
'start-date': '2018-01-01',
'end-date': '2018-02-01',
metrics: 'views',
},
(error, result) => {
if (error) console.log(error.errors);
else {
console.log(result);
callback(result);
}
}
);
}
jwtClient.authorize((error, result) => {
if (error) console.log(error);
else {
/*
{ access_token: '{THE ACCESS TOKEN}',
token_type: 'Bearer',
expiry_date: 1522070090000,
refresh_token: 'jwt-placeholder' } */
console.log(result);
runQuery(() => {
// worked :)
});
}
});
The response object may be helpful:
response:
{ status: 403,
statusText: 'Forbidden',
headers:
{ vary: 'X-Origin, Origin,Accept-Encoding',
'content-type': 'application/json; charset=UTF-8',
date: 'Mon, 26 Mar 2018 10:39:33 GMT',
expires: 'Mon, 26 Mar 2018 10:39:33 GMT',
'cache-control': 'private, max-age=0',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '1; mode=block',
server: 'GSE',
'alt-svc': 'hq=":443"; ma=2592000; quic=51303432; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="42,41,39,35"',
'accept-ranges': 'none',
connection: 'close' },
config:
{ adapter: [Function: httpAdapter],
transformRequest: [Object],
transformResponse: [Object],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: 2147483648,
validateStatus: [Function],
headers: [Object],
method: 'get',
url: 'https://www.googleapis.com/youtube/analytics/v1/reports',
paramsSerializer: [Function],
data: undefined,
params: [Object] },
request:
ClientRequest {
domain: null,
_events: [Object],
_eventsCount: 6,
_maxListeners: undefined,
output: [],
outputEncodings: [],
outputCallbacks: [],
outputSize: 0,
writable: false,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedConnection: false,
_removedContLen: false,
_removedTE: false,
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: [Object],
connection: [Object],
_header: 'GET /youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2018-01-01&end-date=2018-02-01&metrics=views HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nAuthorization: Bearer ya29.c.El6KBUokXWf8yjN1LXk04b3FTGa1jadNEmvbyLBQfVmK9KIbxwxA6e88m_OujuTcyrJW60ojwnKiNfH-9E2iegmwlDOdKI8VxORrniGIt9gc_FGp2tvi2GLabzTri74b\r\nUser-Agent: google-api-nodejs-client/1.3.2\r\nHost: www.googleapis.com\r\nConnection: close\r\n\r\n',
_onPendingData: [Function: noopPendingOutput],
agent: [Object],
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2018-01-01&end-date=2018-02-01&metrics=views',
_ended: true,
res: [Object],
aborted: undefined,
timeoutCb: null,
upgradeOrConnect: false,
parser: null,
maxHeadersCount: null,
_redirectable: [Object],
[Symbol(outHeadersKey)]: [Object] },
data: { error: [Object] } },
code: 403,
errors: [ { domain: 'global', reason: 'forbidden', message: 'Forbidden' } ] }
Update 1
I updated the code a little, so that is analog to this example implementation: https://github.com/google/google-api-nodejs-client/blob/HEAD/samples/jwt.js
I get an authorised client as seen in the code, but the same error occurs. I am pretty sure I messed up the settings in the google cloud settings. Is it necessary to do the domain verification at google cloud when developing locally?
Update 2
Even with OAuth2 Authentication (passing a code as cli arg after allowing scopes in the browser) I get the forbidden-error. Seems like I need to enable it in the cloud dev console, but how do I do that?

Is there a way to get a Session Id from a HTTPS request in node?

I cannot figure out how. I make an HTTPS get request, but I just get a long list of information, but no SessionId
Here is the code I have written in node.js I have replaced my API with a fake one, but other things are the same.
var http = require('http');
//var port = process.env.PORT || 1337;
var https = require('https')
var readline = require('readline');
var url = require('url')
var request = require('request')
var body = ""
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var options = {
url: "https://SOMEAPI.com/us/oauth/v2/authorize?SOMEPATH",
host: "SOMEAPI.com"
}
var cookie = https.get(options, (response) => {
response.on(('data'), (chunk) => {
body += chunk
})
response.on('end', () => {
//printSession(response)
//console.log(response)
})
})
console.log(cookie)
This is my console output:
Debugger listening on [::]:5858
ClientRequest {
domain: null,
_events:
{ response: { [Function: g] listener: [Function] },
socket: { [Function: g] listener: [Function: onSocket] } },
_eventsCount: 2,
_maxListeners: undefined,
output: [ 'GET / HTTP/1.1\r\nHost: [https://SOMEAPI.com]\r\nConnect
ion: close\r\n\r\n' ],
outputEncodings: [ 'latin1' ],
outputCallbacks: [ [Function: finish] ],
outputSize: 77,
writable: true,
_last: true,
upgrading: false,
chunkedEncoding: false,
shouldKeepAlive: false,
useChunkedEncodingByDefault: false,
sendDate: false,
_removedHeader: {},
_contentLength: 0,
_hasBody: true,
_trailer: '',
finished: true,
_headerSent: true,
socket: null,
connection: null,
_header: 'GET / HTTP/1.1\r\nHost: [https://SOMEAPI.com]\r\nConnecti
on: close\r\n\r\n',
_headers: { host: '[https://SOMEAPI.com]' },
_headerNames: { host: 'Host' },
_onPendingData: null,
agent:
Agent {
domain: null,
_events: { free: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
defaultPort: 443,
protocol: 'https:',
options: { path: null },
requests: {},
sockets: { 'https://SOMEAPI.com:443::::::::[https': [Object] },
freeSockets: {},
keepAliveMsecs: 1000,
keepAlive: false,
maxSockets: Infinity,
maxFreeSockets: 256,
maxCachedSessions: 100,
_sessionCache: { map: {}, list: [] } },
socketPath: undefined,
timeout: undefined,
method: 'GET',
path: '/',
_ended: false }
events.js:160
throw er; // Unhandled 'error' event
^
Error: getaddrinfo ENOTFOUND https://SOMEAPI.com https://SOMEAPI.com:443
at errnoException (dns.js:28:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:76:26)
Press any key to continue...
Edit: Here is what I am at now:
Debugger listening on [::]:5858
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring>Policy Falsified</faultstring>
<faultactor>https://SOMEAPI.com/</faultactor>
<detail>
<l7:policyResult
status="Service Not Found. The request may have been sent t
o an invalid URL, or intended for an unsupported operation." xmlns:l7="http://ww
w.layer7tech.com/ws/policy/fault"/>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server</faultcode>
<faultstring>Policy Falsified</faultstring>
<faultactor>https://SOMEAPI.com/</faultactor>
<detail>
<l7:policyResult
status="Service Not Found. The request may have been sent t
o an invalid URL, or intended for an unsupported operation." xmlns:l7="http://ww
w.layer7tech.com/ws/policy/fault"/>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
I believe this is header info that might be important:
REQUEST
GET https://SOMEAPI.com/us/oauth/v2/authorize?SOMEPATH HTTP/1.1
Accept: text/html, application/xhtml+xml, application/xml
Host: SOMEAPI.com
Connection: Keep-Alive

Categories