NestJS: Custom decorator `applyDecorators()` not working with `HttpCode` and `AuthGuard` used together - nestjs

Scenerio:
I have created two custom decorators as a way to cleanup my controllers by applying all the common swagger decorators, at one place. They are as following.
export function AuthorizedEndpoint(header_key?: string) {
return applyDecorators(
// HttpCode(200), // #Todo Why is it not working?
UseGuards(AuthGuard),
ApiBearerAuth(header_key),
ApiOkResponse({ description: ResponseMessageEnum.R_SUCCESS as string }),
ApiUnprocessableEntityResponse({ description: ResponseMessageEnum.R_BAD_REQUEST as string }),
ApiForbiddenResponse({ description: ResponseMessageEnum.R_UNAUTHORIZED as string }),
ApiNotFoundResponse({ description: ResponseMessageEnum.R_NOT_FOUND as string })
);
}
export function OpenAccessEndpoint() {
return applyDecorators(
HttpCode(200),
ApiOkResponse({ description: ResponseMessageEnum.R_SUCCESS as string }),
ApiUnprocessableEntityResponse({ description: ResponseMessageEnum.R_BAD_REQUEST as string }),
ApiForbiddenResponse({ description: ResponseMessageEnum.R_UNAUTHORIZED as string }),
ApiNotFoundResponse({ description: ResponseMessageEnum.R_NOT_FOUND as string })
);
All my controllers are POST, and nestjs sends 201 as response code. But I want to send 200 instead. Therefore, I used the #HttpCode(200) to send required response code. It is working when using on controllers directly.
ISSUE:
In the custom decorator, which does not implement AuthGuard, HttpCode decorator is working as expected, but in the Authorized Decorator, If I use #HttpCode decorator, I get the following message. If I comment it out, it works as expected and sends 201 default response.
[11:30:25 AM] File change detected. Starting incremental compilation...
[11:30:25 AM] Found 0 errors. Watching for file changes.
C: \Users\Lenovo\Project\node_modules\reflect - metadata\Reflect.js: 541;
var decorated = decorator(target);
^
TypeError: Cannot read properties of undefined(reading 'value')
at C: \Users\Lenovo\Project\node_modules\#nestjs\common\decorators\http\http - code.decorator.js: 17: 87
at C: \Users\Lenovo\Project\node_modules\#nestjs\common\decorators\core\apply - decorators.js: 17: 17
at DecorateConstructor(C: \Users\Lenovo\Project\node_modules\reflect - metadata\Reflect.js: 541: 33)
at Reflect.decorate(C: \Users\Lenovo\Project\node_modules\reflect - metadata\Reflect.js: 130: 24)
at __decorate(C: \Users\Lenovo\Project\dist\administration\controllers\organization.controller.js: 4: 92)
at Object.<anonymous>(C: \Users\Lenovo\Project\src\administration\controllers\organization.controller.ts: 14: 37)
at Module._compile(node: internal / modules / cjs / loader: 1239: 14)
at Object.Module._extensions..js(node: internal / modules / cjs / loader: 1293: 10)
at Module.load(node: internal / modules / cjs / loader: 1096: 32)
at Function.Module._load(node: internal / modules / cjs / loader: 935: 12);
Question:
Is this a bug, or some misconfiguration from my end? What is the explaination of above error message? How do I move ahead from it?

Turns out #HttpCode() is a method decorator and therefore cannot be applied to class.
Therefore either we can not include #HttpCode() in custom decorator, and use it manually on each route method, allowing us to use our custom decorator at class level.
Or we can include it in our custom decorator, and loose the ability of using the decorator at class level.

Related

Find front end domain outside a NestJS Controller

Similar to this, I am attempting to get the #Headers('origin') property inside a service which does not have its own controller and called to build email links for various other modules. Since the links are shared by email the front end domain triggering the call has to be identified which is done as follows :
import { Headers, Injectable } from '#nestjs/common';
// ... Other code specific imports
#Injectable()
export class LinkService {
constructor(#Headers('origin') private readonly origin: string) {}
createLink1(LinkId1: string): string {
return `${this.origin}/linkpath1/${LinkId1}`;
}
createLink2(LinkId2: string): string {
return `${this.origin}/linkpath2/${LinkId2}`;
}
// ... Similar follows
}
npm start leads to :
[Nest] 25971 - [ExceptionHandler] Nest can't resolve dependencies of the LinkService (?). Please make sure that the argument String at index [0] is available in the LinkModule context.
Potential solutions:
- If String is a provider, is it part of the current LinkModule?
- If String is exported from a separate #Module, is that module imported within LinkModule?
#Module({
imports: [ /* the Module containing String */ ]
})
+0ms
Error: Nest can't resolve dependencies of the LinkService (?). Please make sure that the argument String at index [0] is available in the LinkModule context.
Potential solutions:
- If String is a provider, is it part of the current LinkModule?
- If String is exported from a separate #Module, is that module imported within LinkModule?
#Module({
imports: [ /* the Module containing String */ ]
})
at Injector.lookupComponentInParentModules (/home/batfan47/graviti/check/graviti-api/node_modules/#nestjs/core/injector/injector.js:188:19)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Injector.resolveComponentInstance (/home/batfan47/graviti/check/graviti-api/node_modules/#nestjs/core/injector/injector.js:144:33)
at async resolveParam (/home/batfan47/graviti/check/graviti-api/node_modules/#nestjs/core/injector/injector.js:98:38)
Would be great to know what would be the best way to go about this, so that the front end domain is available to me as required even in other parts of the code.
that #Headers('origin') on constructor just doens't make any sense. It must be in some method of your controller.
If you want the request object in some service, you could leverage on request scoped provider. Then you can retrieve the headers from it.

How to build a Graqhql mutation with existing variables

This might seem like an odd question, or something really straightforward, but honestly I am struggling to figure out how to do this. I am working in Node.js and I want to set data I have saved on a node object into my GraphQL mutation.
I'm working with a vendor's GraqhQL API, so this isn't something I have created myself, nor do I have a schema file for it. I'm building a mutation that will insert a record into their application, and I can write out everything manually and use a tool like Postman to manually create a new record...the structure of the mutation is not my problem.
What I'm struggling to figure out is how to build the mutation with variables from my node object without just catting a bunch of strings together.
For example, this is what I'm trying to avoid:
class MyClass {
constructor() {
this.username = "my_username"
this.title = "Some Title"
}
}
const obj = new MyClass()
let query = "mutation {
createEntry( input: {
author: { username: \"" + obj.username + "\" }
title: \"" + obj.title + "\"
})
}"
I've noticed that there are a number of different node packages out there for working with Graphql, but none of their documentation that I've seen really addresses the above situation. I've been completely unsuccessful in my Googling attempts, can someone please point me in the right direction? Is there a package out there that's useful for just building queries without requiring a schema or trying to send them at the same time?
GraphQL services typically implement this spec when using HTTP as a transport. That means you can construct a POST request with four parameters:
query - A Document containing GraphQL Operations and Fragments to execute.
operationName - (Optional): The name of the Operation in the Document to execute.
variables - (Optional): Values for any Variables defined by the Operation.
extensions - (Optional): This entry is reserved for implementors to extend the protocol however they see fit.
You can use a Node-friendly version of fetch like cross-fetch, axios, request or any other library of your choice to make the actual HTTP request.
If you have dynamic values you want to substitute inside the query, you should utilize variables to do so. Variables are defined as part of your operation definition at the top of the document:
const query = `
mutation ($input: SomeInputObjectType!) {
createEntry(input: $input) {
# whatever other fields assuming the createEntry
# returns an object and not a scalar
}
}
`
Note that the type you use will depend on the type specified by the input argument -- replace SomeInputObjectType with the appropriate type name. If the vendor did not provide adequate documentation for their service, you should at least have access to a GraphiQL or GraphQL Playground instance where you can look up the argument's type. Otherwise, you can use any generic GraphQL client like Altair and view the schema that way.
Once you've constructed your query, make the request like this:
const variables = {
input: {
title: obj.title,
...
}
}
const response = await fetch(YOUR_GRAPHQL_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables }),
})
const { data, errors } = await response.json()

how to use optional url parameters with NestjS

I'm trying to replace our current backend service using Nestjs library,
however, I want to create a route with 2 optional parameters in the URL something like :
/route/:param1/config/:OptionalParam3?/:OptionalParam3?
that means the route should catch :
route/aa/config
route/aa/config/bb
route/aa/config/bb/cc
how can I achieve that, I have tried to use ? and () but it's not working well.
If you are looking for how to annotate an optional query parameter, you can do it like so:
#ApiQuery({
name: "myParam",
type: String,
description: "A parameter. Optional",
required: false
})
async myEndpoint(
#Query("myParam") myParam?: string
): Promise<blah> {
[...]
}
Router params name should be unique. The correct route path is:
Existing one is:
/route/:param1/config/:OptionalParam3?/:OptionalParam3?
Correction:
/route/:param1/config/:OptionalParam3?/:OptionalParam4?
Opinion: You can use query params if the params are optional. It is never a good idea to create optional param routes (disagreements agreed). Both serve the same purpose, but having them as the query params makes it more understandable for debugging and fellow developers.
I solved this problem by using #Query decorator as below:
Here is my controller:
#Get()
async getAll(#Query('someParameter') someParameter?: number) {
return this.service.getAll(someParameter);
}
Here is my client (Angular) service:
getAll(someParameter?: number) {
return this.http.get(`apiUrl/controllerAddress?someParameter=${someParameter}`
);
}
You can use this structure:
-route
-aa
-config
-[[...id]].js
It will work for the routes :
route/aa/config/{anything}

Karate - When i run a basic feature with get call, i get karate exception as INSTANCE on the line where get is called [duplicate]

I'm trying use karate for e2e tests and have started with a minimal setup. I want to create some config items in karate-config.js for use in the tests but karate is reporting that file is not a js function and hence the test fails trying to get the config:
Warning: Nashorn engine is planned to be removed from a future JDK release
12:16:35.264 [Test worker] WARN com.intuit.karate - not a js function or feature file: read('classpath:karate-config.js') - [type: NULL, value: null]
---------------------------------------------------------
feature: classpath:karate/insurer.feature
scenarios: 1 | passed: 0 | failed: 1 | time: 0.0163
---------------------------------------------------------
HTML report: (paste into browser to view) | Karate version: 0.9.1
file:/Users/srowatt/dev/repos/api/price-service/build/surefire-reports/karate.insurer.html
---------------------------------------------------------
-unknown-:4 - javascript evaluation failed: priceBaseUrl, ReferenceError: "priceBaseUrl" is not defined in <eval> at line number 1
org.opentest4j.AssertionFailedError: -unknown-:4 - javascript evaluation failed: priceBaseUrl, ReferenceError: "priceBaseUrl" is not defined in <eval> at line number 1
This is my karate-config.js:
function fn() {
return {
priceBaseUrl: "http://localhost:8080"
};
}
This is my insurer.feature test:
Feature: which creates insurers
Background:
* url priceBaseUrl
* configure logPrettyRequest = true
* configure logPrettyResponse = true
Scenario: basic roundtrip
# create a new insurer
Given path 'insurers'
And request { name: 'Sammy Insurance', companyCode: '99' }
When method post
Then status 201
And match response == { resourceId: '#number', version: 0, createdBy: 'anonymousUser' }
* def insurerId = response.resourceId
# get insurer by resource id
Given path 'insurers', insurerId
When method get
Then status 200
And match response == { id: '#(id)', name: 'Sammy Insurance', companyCode: '99' }
This is the InsurerTest.java test runner:
package karate;
import com.intuit.karate.junit5.Karate;
class InsurerTest {
#Karate.Test
public Karate testInsurer() {
return new Karate().feature("classpath:karate/insurer.feature");
}
}
Please use below code in the karate-config.js
function() {
return priceBaseUrl='http://localhost:8080';
}
When I see this:
Warning: Nashorn engine is planned to be removed from a future JDK release
I suspect you are on Java 9 or 11 ? To be honest, we haven't fully tested Karate on those versions of Java yet. Would it be possible for you to confirm that Java 8 (maybe 9 / 10 also) is OK.
That said, we are interested in resolving this as soon as possible, so if you can submit a sample project where we can replicate this, please do so: https://github.com/intuit/karate/wiki/How-to-Submit-an-Issue
EDIT: Karate 1.0 will use GraalVM instead of Nashorn and will run on even JDK 16: https://software-that-matters.com/2021/01/27/7-new-features-in-karate-test-automation-version-1_0/

Shoulda route matcher with subdomains

I am using shoulda matchers to test critical routes on Rails 4.2.1 with Ruby 2.2.0 on a new application. I just moved my API namespace to a subdomain, and I can't figure out how to get the shoulda routes matcher (or any other concise routes test) to work.
Here is some example code:
config/routes.rb (using versionist for versioning, but that shouldn't be relevant)
namespace :api, path: '', constraints: { subdomain: 'api'} do
api_version(module: 'V1',
path: {value: 'v1'},
defaults: {format: 'json'}, default: true) do
resources :bills, only: :index
end
end
app/controllers/api/v1/bills_controller.rb
module API
module V1
class Bill < APIVersionsController
# GET api.example.com/v1/bills.json
def index
#bills = Bill.all.limit(10)
render json: #bills
end
end
end
end
test/controllers/api/v1/routing_test.rb
module API
module V1
class RoutingTest < ActionController::TestCase
setup { #request.host = 'http://api.example.com' }
should route('/v1/bills')
.to(controller: :bill, action: :index, format: :json)
end
end
end
Before I was using a subdomain, should route('/api/v1/bills').to(action: :index, format: :json) in my BillsControllerTest worked just fine.
Now, when I run rake test, I get Minitest::Assertion: No route matches "/v1/bills".
I've tried putting this in the BillControllerTest (no change);
I've tried an integration test (route matcher doesn't work);
I've tried setting the host with setup { host! 'api.example.com' } and setup { #request.host = 'api.example.com' }
I've tried putting the full URL in the get request ( { get 'http://api.example.com/v1/bills' } );
and I've tried putting subdomain: 'api' and constraints: subdomain: 'api' anywhere that might make sense.
What is a concise way to do route testing with subdomains/what is the current best practice? Is there a way to get the shoulda route matcher to work with them?
This ended up being a simple fix. I just needed to add a subdomain constraint in the #to method and ensure the #route method had the full url:
module API
module V1
class RoutingTest < ActionController::TestCase
should route(:get, 'http://api.example.com/v1')
.to('api/v1/data#index',
subdomain: 'api',
format: :json)
...
Or, if you are in data_controller_test.rb,
module API
module V1
class DataControllerTest < ActionController::TestCase
should route(:get, 'http://api.example.com/v1')
.to(action: :index,
subdomain: 'api',
format: :json)
...

Resources