How to access request headers in loopback4? - loopbackjs

I used a simple statement like below to access headers in loopback4.
console.log(request.headers);
But it is printing undefined. A sample request headers that I want to access is in the image.
request header image
I am receiving the request and its headers which is perfectly fine. It's just that I am not able to access its headers as I am getting undefined from request.headers.
I am a beginner in loopback so pls explain it.
If i have to use a bodyparser then how i would have to use it in loopback4 because it is different from express.

Update
The original answer, although valid, is not the recommended way. Use dependency injection instead:
import {inject} from '#loopback/core';
import {get, param} from '#loopback/rest';
export class SomethingController {
constructor() {}
#get('/something')
something(#param.header.string('x-your-header') yourHeader: string): void {
// Use your header.
// e.g. Log to console
console.log(yourHeader);
}
}
Unlike the REQUEST object, this strips away unnecessary info and provides built-in coercion.
Further reading
https://loopback.io/doc/en/lb4/Decorators_openapi.html#parameter-decorator
https://loopback.io/doc/en/lb4/apidocs.openapi-v3.param.html
Original answer
If you're attempting to access the headers in a Controller, then you can inject the REQUEST object:
import {inject} from '#loopback/core';
import {get, Request, RestBindings} from '#loopback/rest';
export class SomethingController {
constructor(#inject(RestBindings.Http.REQUEST) private req: Request) {}
#get('/something')
something(): void {
// Get the headers
this.req.headers;
}
}

You can also use Context, example
import {inject} from '#loopback/core';
import {
post, RequestContext
} from '#loopback/rest';
export class UserController {
constructor(
#inject.context()
public context: RequestContext,
) {}
#post('/users/logout', {
responses: {
'200': {
description: 'Return success',
content: {
'application/json': {
schema: {
type: 'object'
},
},
},
},
},
})
async logout(): Promise<object> {
// ensure the token exists
const authHeader = this.context.request.headers.authorization;
if(authHeader && authHeader.split(" ")[1]){
// remove token
return {code:204, status: true, message:'Logout successful'};
}else{
return {code: 404, status: false, message:'Something went wrong'};
}
}
}

In sequence.ts, request object is called via context.
const {request, response} = context;
console.log(request.headers)
add log in sequence.ts to get the request headers.

Related

Apollo Gateway: Forward Subgraph `set-cookie` Header to Gateway to Client

I have a subgraph microservice that handles sessions. We store our sessions via cookies that the subgraph creates, and should set it via the set-cookie header. Only issue is my gateway does not seem to be forwarding the set-cookie header from the subgraph to the client.
Here is the code for my gateway
const { ApolloServer } = require('apollo-server');
const { ApolloGateway, RemoteGraphQLDataSource } = require('#apollo/gateway');
const { readFileSync } = require('fs');
const supergraphSdl = readFileSync('./gateway/supergraph.graphql').toString();
class CookieDataSource extends RemoteGraphQLDataSource {
didReceiveResponse({ response, request, context }) {
const cookie = response.http.headers.get('set-cookie');
console.log("Cookie:", cookie)
return response;
}
}
const gateway = new ApolloGateway({
supergraphSdl,
buildService({url}) {
return new CookieDataSource({url});
}
});
const server = new ApolloServer({
gateway,
cors: {
origin: ["http://localhost:3000", "https://studio.apollographql.com"],
credentials: true
},
csrfPrevention: true,
});
server.listen().then(({ url }) => {
console.log(`🚀 Gateway ready at ${url}`);
}).catch(err => {console.error(err)});
version info
“#apollo/gateway”: “^2.1.2”,
“apollo-server”: “^3.10.2”,
I can confirm that the subgraph is sending back a set-cookie header, however, it is not being passed through to the client.
Thank you!
I ended up resolving the issue by creating both a gateway datasource that added context value. Then, pass the header from the subgraph context value to the response header.
import { GatewayGraphQLResponse, GatewayGraphQLRequestContext } from '#apollo/server-gateway-interface';
import { RemoteGraphQLDataSource } from '#apollo/gateway';
import { ApolloServerPlugin, GraphQLRequestContext, GraphQLRequestListener } from '#apollo/server';
interface ServerContext {
passthrough_cookies?: string
}
export class CookieProcessorDataSource extends RemoteGraphQLDataSource {
didReceiveResponse({response, context}: Required<Pick<GatewayGraphQLRequestContext<Record<string, any>>, 'request' | 'response' | 'context'>>): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse> {
context.passthrough_cookies = response.http?.headers.get('set-cookie');
return response;
}
}
export class CookieServerListener implements GraphQLRequestListener<ServerContext> {
public willSendResponse({contextValue, response}: GraphQLRequestContext<ServerContext>): Promise<void> {
if (contextValue.passthrough_cookies !== undefined) {
response.http.headers.set('set-cookie', contextValue.passthrough_cookies);
}
return Promise.resolve()
}
}
export class CookieServerPlugin implements ApolloServerPlugin<ServerContext> {
async requestDidStart() {
return new CookieServerListener();
}
}

Trouble Writing to Jest Mocked Prisma Database

I have two databases that I need to interact with in my code. I have a simple function that takes an object and writes it to my PostgreSQL database using Prisma. I've tested the function with Postman, and it works perfectly, but when I try to execute it using a Jest mock (using the singleton pattern found in the Prisma unit testing guide), it returns undefined indicating that it didn't interact with the database and create the new record. Here's my code:
/prisma/clinical-schema.prisma
generator client {
provider = "prisma-client-js"
output = "./generated/clinical"
}
datasource clinicalDatabase {
provider = "postgresql"
url = "postgresql://postgres:postgres#localhost:5432/clinical-data?schema=public"
}
model pcc_webhook_update {
id Int #id #default(autoincrement())
event_type String
organization_id Int
facility_id Int
patient_id Int
resource_id String?
webhook_date DateTime #default(now()) #clinicalDatabase.Timestamptz(6)
status pcc_webhook_update_status #default(pending)
status_changed_date DateTime? #clinicalDatabase.Timestamptz(6)
error_count Int #default(0)
##unique([organization_id, facility_id, patient_id, resource_id, event_type, status])
}
enum pcc_webhook_update_status {
pending
processing
processed
error
}
/prisma/clinical-client.ts
import { PrismaClient } from './generated/clinical';
const prismaClinical = new PrismaClient();
export default prismaClinical;
/testing/prisma-clinical-mock.ts
import { PrismaClient } from '../prisma/generated/clinical';
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';
import prisma from '../prisma/clinical-client';
jest.mock('../prisma/clinical-client', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}));
beforeEach(() => {
mockReset(prismaClinicalMock);
});
export const prismaClinicalMock = prisma as unknown as DeepMockProxy<PrismaClient>;
Everything up to this point follows the conventions outlined by the Prisma unit testing docs. The only modification I made was to make it database specific. Below is my function and tests. The request object in handle-pcc-webhooks.ts is a sample http request object, the body of which contains the webhook data I care about.
/functions/handle-pcc-webhooks/handler.ts
import prismaClinical from '../../../prisma/clinical-client';
import { pcc_webhook_update } from '../../../prisma/generated/clinical';
import { requestObject } from './handler.types';
export const handlePccWebhook = async (request: requestObject) => {
try {
const webhook = JSON.parse(request.body);
// if the webhook doesn't include a resource id array, set it to an array with an empty string to ensure processing and avoid violating
// the multi-column unique constraint on the table
const { resourceId: resourceIds = [''] } = webhook;
let records = [];
for (const resourceId of resourceIds) {
// update an existing record if one exists in the pending state, otherwise create a new entry
const record: pcc_webhook_update = await prismaClinical.pcc_webhook_update.upsert({
where: {
organization_id_facility_id_patient_id_resource_id_event_type_status: {
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
event_type: webhook.eventType,
status: 'pending'
}
},
update: {
webhook_date: new Date()
},
create: {
event_type: webhook.eventType,
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
status: 'pending' // not needed
}
});
records.push(record);
}
return records;
} catch (error) {
console.error(error);
}
};
/functions/handle-pcc-webhooks/handler.spec.ts
import fs from 'fs';
import path from 'path';
import MockDate from 'mockdate';
import { prismaClinicalMock } from '../../../testing/prisma-clinical-mock';
import { createAllergyAddRecord } from './__mocks__/allergy';
import { requestObject } from './handler.types';
import { handlePccWebhook } from './handler';
describe('allergy.add', () => {
let requestObject: requestObject;
let allergyAddRecord: any;
beforeAll(() => {
requestObject = getRequestObject('allergy.add');
});
beforeEach(() => {
MockDate.set(new Date('1/1/2022'));
allergyAddRecord = createAllergyAddRecord(new Date());
});
afterEach(() => {
MockDate.reset();
});
test('should create an allergy.add database entry', async() => {
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
// this is where I would expect handlePccWebhook to return the newly created database
// record, but instead it returns undefined. If I run the function outside of this
// unit test, with the same input value, it functions perfectly
await expect(handlePccWebhook(requestObject)).resolves.toEqual([allergyAddRecord]);
});
});
// This just builds a request object with the current webhook being tested
function getRequestObject(webhookType: string) {
// read the contents of request object file as a buffer, then convert it to JSON
const rawRequestObject = fs.readFileSync(path.resolve(__dirname, '../../sample-data/handle-pcc-webhook-request.json'));
const requestObject: requestObject = JSON.parse(rawRequestObject.toString());
// read the contents of the webhook file as a buffer, then convert it to a string
const rawWebhook = fs.readFileSync(path.resolve(__dirname, `../../sample-data/${webhookType}.json`));
const webhookString = rawWebhook.toString();
// set the body of the request object to the contents of the target webhook
requestObject.body = webhookString;
return requestObject;
}
Finally, here is the result of running the unit test:
So after banging my had against the wall for a few hours, I figured out the issue. In my handler.spec.ts file, I had the following line:
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
what that does is mock the value returned for any create functions run using Prisma. The issue is that my function is using an upsert function, which I wasn't explicitly mocking, thus returning undefined. I changed the above line to
prismaClinicalMock.pcc_webhook_update.upsert.mockResolvedValue(allergyAddRecord);
and it started working.

AdonisJS 'bouncer' package not working as intended

As described here, I have implemented the authorization:
start/bouncer.ts:
import Bouncer from '#ioc:Adonis/Addons/Bouncer'
export const { actions } = Bouncer
export const { policies } = Bouncer.registerPolicies({
UserPolicy: () => import('App/Policies/UserPolicy'),
})
app/Policies/UserPolicy.ts:
import { BasePolicy } from '#ioc:Adonis/Addons/Bouncer'
import User from 'App/Models/User'
export default class UserPolicy extends BasePolicy {
public async before(user?: User) {
return user?.isSuperUser
}
public async list(user: User) {
await user.load('policies')
return user.policies.some((policy) => policy.identifier === 'user:list')
}
// ...
}
resources/vires/layouts/main.edge
#can('UserPolicy.list')
<p>Can see users list</p>
#endcan
And I cannot see the paragraph. In fact, I placed console.log inside the action, but it didn't get executed. I don't know if I'm missing anything. Can anyone shed some lights onto it?
Gotcha! This says:
The actual action callback is never executed when a before hook returns a true or a false value.
Make sure to return undefined if you want the bouncer to execute the next hook or the action callback.
These 2 statements were missed out. :)

Passing a pdf from Django backend to Angular frontend

I haven't been able to make any of the solutions to similar problems work for my case.
I would like to load a pdf from filesystem with django and return it via an API call to Angular so that it can be displayed. My Django code is pretty much:
class LoadPdfViewSet(views.APIView):
def get(self, request):
# some code here here
response = FileResponse(open(path_to_pdf, 'rb').read())
response.headers = {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment;filename="report.pdf"',
}
response.as_attachment = True
return response
while on the Angular side I have a service that does this:
export class LoadPdfService {
constructor(
private http: HttpClient
) {}
getPdf(): Observable<Blob> {
const params = new HttpParams({
fromObject: {
responsetype: 'arraybuffer'
// other stuff here
}
})
return self.http.get<Blob>(loadpdf_api_url, {params}).pipe(catchError(self.myErrorHandler))
}
}
and a component that tries to open the pdf like this:
export class MyComponent {
constructor(
public loadPdfService: LoadPdfService
) {}
download_pdf() {
let call = self.loadPdfService.getPdf();
call.subscribe( (response:Blob) => {
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(blob, "report.pdf");
} else {
let pdfUrl = URL.createObjectURL(blob)
window.open(pdfUrl, '_blank')
URL.revokeObjectURL(pdfUrl);
}
}
}
}
but nothing happens. I have also tried using different responses and passthrough renderers on the django side, and Observable<Response> and .then() callbacks like
response.arrayBuffer().then(buffer => new Blob([buffer], {type: 'application/pdf'}))
on the Angular side. Sometimes I have managed to get the new window/tab to open but no pdf could be displayed.
I finally figured it out. In the python part, the read() can be removed with no problem. The issue was with the service response type and mapping of the response:
getPdf(): Observable<Blob> {
const options = {
params: new HttpParams({
fromObject: {
// my own parameters here
}
}),
responseType: 'blob' as 'json'
};
return this.http.get(this.url, options).pipe(
map(response => response as Blob),
catchError(this.myErrorHandler))
}

How can I return JSON with HTTP code in Vibe.d?

I would like to return not only JSON, but also the HTTP response code.
I registering REST interface through URLRouter:
router.registerRestInterface(new ClientServerAPI);
Example of my REST implementation:
module clienserverapi.clientserver;
import api.clientserver;
import models.replies.client_versions;
/**
Implementation of Client-Server API.
*/
class ClientServerAPI : IClientServerAPI {
#safe:
ClientVersions getSupportedClientVersions() {
bool[string] unstableFeatures;
return ClientVersions(supportedVersions.dup, unstableFeatures);
}
}
In the REST interface generator the response codes are handled automatically and as you can't pass in a HTTPServerResponse/HTTPServerRequest argument into your REST methods, you can't control what status gets returned.
However there are some built-in statuses which get handled:
200/204 are returned based on content
400 Bad request for mismatched arguments
404 Not found for unmatched routes
500 internal server error is returned on most exceptions
(outside of debug mode) unauthorized / bad request / forbidden are sent
See also: REST interface documentation
and you can control any status code using HTTPStatusException, however it is treated as error and will result in a predefined error json which has statusMessage as exception message set and returns the HTTP status code you pass to it. (this is probably what you want for error handling)
You can also change what the errors look like by setting the errorHandler to a RestErrorHandler delegate in your RestInterfaceSettings.
Alternatively, depending on what you want to do, you can use a WebInterface which is a lot like a rest interface, but doesn't have some convenience functions REST interfaces do, but instead can fully access the request/response arguments and can basically do anything like a normal http route and has some other convenience functions you can use.
In theory you could abuse the errorHandler + HTTPStatusException with valid HTTP status codes if you want to return custom success codes with your data, but I would discourage that and instead go with web interfaces if that's what you are after.
However if all you want to do is having custom error codes with a custom, but consistent, error page then I would definitely go with REST interface with an errorHandler.
Your could could now look like this:
import vibe.vibe;
import std.uni;
#safe:
void main() {
auto server = new HTTPServerSettings;
server.port = 3000;
server.bindAddresses = ["::1", "127.0.0.1"];
auto router = new URLRouter;
RestInterfaceSettings settings = new RestInterfaceSettings();
// this is how the error page will look on any thrown exception (like HTTPStatusException)
settings.errorHandler = (HTTPServerRequest req, HTTPServerResponse res,
RestErrorInformation error) #safe {
res.writeJsonBody([
// design this however you like
"ok": Json(false),
"error": serializeToJson([
"status": Json(cast(int)error.statusCode),
"message": Json(error.exception.msg),
"parent": Json("/api/something")
])
]);
};
router.registerRestInterface(new Impl, settings);
listenHTTP(server, router);
runApplication();
}
interface RestAPI {
string getGreeting(string name);
}
class Impl : RestAPI {
string getGreeting(string name)
{
// throw an HTTP Bad Request error when name is empty
if (name.length == 0)
throw new HTTPStatusException(HTTPStatus.badRequest, "Name parameter cannot be empty!");
// throw an HTTP Conflict error code when name is Bob
if (sicmp(name, "bob") == 0)
throw new HTTPStatusException(HTTPStatus.conflict, "Server cannot greet Bob!");
return "Hello, " ~ name ~ "!";
}
}
and your server will then respond something like:
{
"ok": false,
"error": {
"message": "Server cannot greet Bob!",
"status": 409,
"parent": "/api/something"
}
}
You can try hunt framework, sample code for Rest api:
module app.controller.myapi;
import hunt.framework;
import app.message.UserMessage;
class MyapiController : Controller
{
mixin MakeController;
#Action
JsonResponse test()
{
UserMessage user;
user.id = 1;
user.name = "MyName";
user.email = "test#domain.com";
return new JsonResponse(user);
}
}
Your response struct:
module app.message.ResultMessage;
struct UserMessage
{
int id;
string name;
string email;
}
Response result is:
[ "id": 1, "name": "MyName", "email": "test#domain.com" ]