Validator is not working on Resource routes in AdonisJS - validation

I'm having problems with validator on Route.resource(). The validator is not been applied and when I run the command "adonis route:list" the av:TrainingPlan and Workout validators are not being listed. You can see that my other validators are working on single endpoint verb like "Route.post" on /users, /sessions/ and /passwords. On app.js under start folder I checked that the register was made too and all validator was generated by adonis cli.
My routes file was made based on documentation:
Route.group(() => {
Route.post('files', 'FileController.store')
Route
.resource('/training-plans', 'TrainingPlanController')
.apiOnly()
.validator(new Map([
[['training-plans.store'], ['TrainingPlan']]
]))
Route
.resource('/workouts', 'WorkoutController')
.apiOnly()
.validator(new Map([
[['workouts.store'], ['Workout']]
]))
}).middleware(['auth'])
TrainingPlan Validator:
'use strict'
class TrainingPlan {
get validateAll () {
return true
}
get rules () {
return {
title: 'required',
description: 'required',
start_date: `date|before:${new Date()}`,
end_date: `date|before:${new Date()}`
}
}
}
module.exports = TrainingPlan
What I'm missing out?
adonis route:list command result

The problem was resolved removing / before /workouts and /training-plans route.
I did not tested putting / before workouts.store and training-plans.store inside Map validator.

Related

Cypress: The 'task' event has not been registered in the plugins file. You must register it before using cy.task()

I am writing the end-to-end tests using Cypress for my web application. In my tests, I am trying to create a task, https://docs.cypress.io/api/commands/task. But it is throwing an error. Here is what I did.
I declared a task in the plugins/index.js file as follow.
module.exports = (on) => {
on("task", {
setTestId(id) {
testId = id;
return null;
},
getTestId() {
return testId;
}
});
};
Then I use the task in the test as follow.
cy.task('setTestId', 7654321);
When I run the tests, I am getting the following error.
The 'task' event has not been registered in the plugins file. You must register it before using cy.task()
As you can see, I tried this solution as well, Cypress task fails and complains that task event has not been registered in the plugins file. It did not work either. What is wrong with my code and how can I fix it?
You're missing some formatting (":" and "=>" and such). Try this:
module.exports = (on) => {
on("task", {
setTestId: (id) => {
testId = id;
return null;
},
getTestId: () => {
return testId;
}
});
};
Set the testId as you did:
cy.task('setTestId', 7654321);
And when you want to retrieve the testId, use:
cy.task('getTestId').then((testId) => {
cy.log(testId)
});

Spartacus Storefront Multisite I18n with Backend

We've run into some problems for our MultiSite Spartacus setup when doing I18n.
We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
But the Spartacus setup doesn't let us pass the baseSite? We can
pass {{lng}} and {{ns}}, but no baseSite.
See https://sap.github.io/spartacus-docs/i18n/#lazy-loading
We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.
In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues
Does someone have ideas for these problems?
Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.
To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():
#Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
readonly configFactory = () => this.resolveConfig().toPromise();
constructor(protected baseSiteService: BaseSiteService) {}
protected resolveConfig(): Observable<I18nConfig> {
return this.baseSiteService.getActive().pipe(
take(1),
map((baseSite) => ({
i18n: {
backend: {
// initialize your i18n backend path using the basesite value:
loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
},
},
}))
);
}
}
and provide it in your module (i.e. in app.module):
#NgModule({
providers: [
{
provide: CONFIG_INITIALIZER,
useExisting: I18nBackendPathConfigInitializer,
multi: true,
},
],
/* ... */
})
Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).

Stripe PaymentIntent with confirmation method manual fails every time

I'm using Laravel with a personal integration of the Stripe API (using Stripe API from github).
Everything was working fine until i switched to manual confirmation mode, and now i'm receiving the following error:
This PaymentIntent pi_**************uVme cannot be confirmed using your publishable key because its `confirmation_method` is set to `manual`. Please use your secret key instead, or create a PaymentIntent with `confirmation_method` set to `automatic`.
Any idea?
This is my current code (which is not working):
Stripe::setApiKey(config('services.stripe.secret')); // config('services.stripe.secret') returns "sk_test_gFi********************nMepv"
$paymentIntent = PaymentIntent::create([
'amount' => $orderSession->order_total * 100,
'currency' => 'eur',
'description' => "Pagamento di ".(price($orderSession->order_total))."€ a ".$orderSession->user->user_name." in data ".(now()->format("d-m-Y H:m:s")),
'metadata' => [
'subtotal' => $orderSession->order_subtotal,
'user'=> "{$orderSession->user_id} : {$orderSession->user->user_email}",
'wines'=> substr(
$orderSession->wines()->select('wine_id', 'quantity')->get()->each(
function($el){
$el->q= $el->quantity;
$el->id = $el->wine_id;
unset($el->wine_id, $el->pivot, $el->quantity);
}
)->toJson(),
0,
500
),
],
'confirmation_method' => 'manual',
]);
JS frontend:
<button class="myButtonPayment" id="card-button" type="button" data-secret="{!!$stripePaymentIntent->client_secret!!}" ><span>Pay</span></button>
...
<script>
cardButton.addEventListener('click', function() {
if(!document.getElementById('order_telephone_number').value || /^\+?[0-9 ]{6,20}$/.test(document.getElementById('order_telephone_number').value)){
stripe.handleCardPayment(
clientSecret, cardElement, {
payment_method_data: {
billing_details: {name: cardholderName.value}
}
}
).then(function (result) {
if (result.error) {
console.log(result.error);
} else {
document.getElementById('myForm').submit();
}
});
}
});
</script>
The error is occuring when I click on the button (so is not related to the part of the code where I confirm the payment)
The error serialization is the following:
{
"type":"invalid_request_error",
"code":"payment_intent_invalid_parameter",
"doc_url":"https://stripe.com/docs/error-codes/payment-intent-invalid-parameter",
"message":"This PaymentIntent pi_1H3TQ*********T00uVme cannot be confirmed using your publishable key because its `confirmation_method` is set to `manual`. Please use your secret key instead, or create a PaymentIntent with `confirmation_method` set to `automatic`.",
"payment_intent":{
"id":"pi_1H3***********uVme",
"object":"payment_intent",
"amount":2060,
"canceled_at":null,
"cancellation_reason":null,
"capture_method":"automatic",
"client_secret":"pi_1H3TQ********T00uVme_secret_2T7Di*********nkoaceKx",
"confirmation_method":"manual",
"created":1594415166,
"currency":"eur",
"description":"....",
"last_payment_error":null,
"livemode":false,
"next_action":null,
"payment_method":null,
"payment_method_types":[
"card"
],
"receipt_email":null,
"setup_future_usage":null,
"shipping":null,
"source":null,
"status":"requires_payment_method"
}
}
Manual confirmation for Payment Intents is for server-side confirmation only (i.e. with your secret API key, not your publishable key). Setting confirmation_method to manual on a Payment Intent is the same as saying, "this Payment Intent can only be confirmed server-side".
You can read more about this in in the finalize payments on the server guide in Stripe's documentation.

DTO not working for microservice, but working for apis directly

I am developing apis & microservices in nestJS,
this is my controller function
#Post()
#MessagePattern({ service: TRANSACTION_SERVICE, msg: 'create' })
create( #Body() createTransactionDto: TransactionDto_create ) : Promise<Transaction>{
return this.transactionsService.create(createTransactionDto)
}
when i call post api, dto validation works fine, but when i call this using microservice validation does not work and it passes to service without rejecting with error.
here is my DTO
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class TransactionDto_create{
#IsNotEmpty()
action: string;
// #IsString()
readonly rec_id : string;
#IsNotEmpty()
readonly data : Object;
extras : Object;
// readonly extras2 : Object;
}
when i call api without action parameter it shows error action required but when i call this from microservice using
const pattern = { service: TRANSACTION_SERVICE, msg: 'create' };
const data = {id: '5d1de5d787db5151903c80b9', extras:{'asdf':'dsf'}};
return this.client.send<number>(pattern, data)
it does not throw error and goes to service.
I have added globalpipe validation also.
app.useGlobalPipes(new ValidationPipe({
disableErrorMessages: false, // set true to hide detailed error message
whitelist: false, // set true to strip params which are not in DTO
transform: false // set true if you want DTO to convert params to DTO class by default its false
}));
how will it work for both api & microservice, because i need all at one place and with same functionality so that as per clients it can be called.
ValidationPipe throws HTTP BadRequestException, where as the proxy client expects RpcException.
#Catch(HttpException)
export class RpcValidationFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
return new RpcException(exception.getResponse())
}
}
#UseFilters(new RpcValidationFilter())
#MessagePattern('validate')
async validate(
#Payload(new ValidationPipe({ whitelist: true })) payload: SomeDTO,
) {
// payload validates to SomeDto
. . .
}
I'm going out on a limb and assuming in you main.ts you have the line app.useGlobalPipes(new ValidationPipe());. From the documentation
In the case of hybrid apps the useGlobalPipes() method doesn't set up pipes for gateways and micro services. For "standard" (non-hybrid) microservice apps, useGlobalPipes() does mount pipes globally.
You could instead bind the pipe globally from the AppModule, or you could use the #UsePipes() decorator on each route that will be needing validation via the ValidationPipe
More info on binding pipes here
As I understood, useGlobalPipes is working fine for api but not for microservice.
Reason behind this, nest microservice is a hybrid application and it has some restrictions. Please refer below para.
By default a hybrid application will not inherit global pipes, interceptors, guards and filters configured for the main (HTTP-based) application. To inherit these configuration properties from the main application, set the inheritAppConfig property in the second argument (an optional options object) of the connectMicroservice() call.
Please refer this Nest Official Document
So, you need to add inheritAppConfig option in connectMicroservice() method.
const microservice = app.connectMicroservice(
{
transport: Transport.TCP,
},
{ inheritAppConfig: true },
);
It worked for me!

Laravel Passport and Heroku where to store encryption keys?

I am trying to deploy a Laravel application, which uses Laravel Passport, with Heroku.
Everytime the app is deployed the slug is rebuilt causing the Laravel Passport encryption keys to be erased. It means everytime the app is deployed:
the storage folder permissions should be changed
new encryption keys should be generated
all existing tokens would be undecryptable
For now, the solution I ended up with is to store the OAuth private and public keys in two different Heroku config variables (a.k.a. environment variables). And I extended the PassportServiceProvider class so it looks for the encryption keys in environment variables if the environment is set to "production":
I created a new PassportServiceProvider class in app/Providers:
<?php
namespace App\Providers;
use Laravel\Passport\PassportServiceProvider as LaravelPassportServiceProvider;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\ResourceServer;
class PassportServiceProvider extends LaravelPassportServiceProvider
{
protected function registerResourceServer()
{
$publicKeyPath = config('app.env') === 'production' ? env('OAUTH_PUBLIC_KEY') : 'oauth-public.key';
$this->app->singleton(ResourceServer::class, function () use ($publicKeyPath) {
return new ResourceServer(
$this->app->make(\Laravel\Passport\Bridge\AccessTokenRepository::class),
$this->makeCryptKey($publicKeyPath)
);
});
}
public function makeAuthorizationServer()
{
$privateKeyPath = config('app.env') === 'production' ? env('OAUTH_PRIVATE_KEY') : 'oauth-private.key';
return new AuthorizationServer(
$this->app->make(\Laravel\Passport\Bridge\ClientRepository::class),
$this->app->make(\Laravel\Passport\Bridge\AccessTokenRepository::class),
$this->app->make(\Laravel\Passport\Bridge\ScopeRepository::class),
$this->makeCryptKey($privateKeyPath),
app('encrypter')->getKey()
);
}
protected function makeCryptKey($key)
{
if (config('app.env') === 'production') {
return new CryptKey(str_replace('\n', "\n", $key), null, false);
}
return new CryptKey(file://'.Passport::keyPath($key), null, false);
}
}
Append the following line to the providers array in config/app.php:
Api\Providers\PassportServiceProvider::class,
Tell Laravel to not auto-discover the Laravel Passport package. in composer.json:
"extra": {
"laravel": {
"dont-discover": [
"laravel/passport"
]
}
}
This solution is not fully functional on its own because of this issue.
But I am not convinced it is the right way to do it. I wonder if someone already faced the problem. If so how did you solve it?
Thank you in advance.

Resources