Adonis.js api delete route is not working - adonis.js

I tried to hit a specific route:
http://127.0.0.1:3333/store/products?productId=4
but the server give me this error:
"message": "E_ROUTE_NOT_FOUND: Cannot DELETE:/store/products",
"stack": "HttpException: E_ROUTE_NOT_FOUND: Cannot PATCH:/store/products\n

In addition to the points raised by #crbast :
your code seems to hit the HTTP PATCH method (https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) instead of the HTTP DELETE you expect

You are not hitting the right url and your route is wrong.
The right url with your route.js is :
http://127.0.0.1:3333/store/products/4
^- Product id
and the route :
Route.delete('/products/:productId', 'ProductsController.delete')
// ^- use : for url parameter
Routing explanation
Body data & url parameters are totally different.
Please read : What is the difference between URL parameters and query strings?
Body data
Request body (json).
Documentation : https://preview.adonisjs.com/guides/http/form-submissions#reading-form-data
Example url :
http://127.0.0.1:3333/products?name=hello
Route example :
Route.post('/products', 'MyController.myFunction')
Controller :
public async myFunction ({ request }: HttpContextContract) {
const data = request.only(['name'])
// ...
}
Url parameter
Specify dynamic url parameter.
Documentation : https://preview.adonisjs.com/guides/http/routing#dynamic-urls
Example url :
http://127.0.0.1:3333/products/1
Route example :
Route.post('/products/:id', 'MyController.myFunction')
Controller :
public async myFunction ({ params }: HttpContextContract) {
const id = params.id
// ...
}

Related

Loopback 4 OpenAPI connector: Specify Authorization header value per request

I have set up an OpenAPI connector in Loopback 4 as described here and for unauthorized requests, it is working well; I managed to create the respective datasource, service and controller. My service is similar to the GeocoderProvider example, but, let's say, with the following service interface.
export interface MyExternalService {
search_stuff(params: {query?: string}): Promise<MyExternalServiceResponse>;
}
export interface MyExternalServiceResponse {
text: string;
}
From my controller, I invoke it like this, where this.myExternalService is the injected service (kind of unrelated, but can Loopback also implicitly parse a JSON response from an external API datasource?):
#get('/search')
async searchStuff(#param.query.string('query') query: string): Promise<void> {
return JSON.parse(
(await this.myExternalService.search_stuff({query})).text,
);
}
Now, the external endpoint corresponding to myExternalService.search_stuff needs an Authorization: Bearer <token> header, where the token is sent to Loopback by the client, i.e. it's not a static API key or so. Assuming I added #param.query.string('token') token: string to the parameter list of my searchStuff controller method, how can I forward that token to the OpenAPI connector? This is the relevant part of the underlying OpenAPI YAML definition file:
paths:
/search:
get:
security:
- Authorization: []
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/SearchResults'
operationId: search-stuff
components:
securitySchemes:
Authorization:
type: http
scheme: Bearer
I am now using the underlying execute function of the OpenAPI connector and manually intercept the request (the object that is passed to requestInterceptor is later passed directly to the http module by Swagger):
return JSON.parse(
(
await this.myExternalService.execute(
'search_stuff',
{query},
{
requestInterceptor: (req: {headers: {Authorization: string}}) => {
req.headers.Authorization = 'Bearer ' + token;
return req;
},
},
)
).text,
);
I also added the following method to the MyExternalService interface, inspired by the connector's actual execute function:
execute(
operationId: string,
parameters: object,
options: object,
): Promise<MyExternalServiceResponse>;
Some things I found:
Loopback internally uses the swagger-client module to do OpenAPI-based requests.
Specifically the securities option of Swagger's execute function expects a Security Definitions Object. There are some quirks with actually passing it to Swagger as well.
Internally, Swagger builds the final HTTP request that is sent out here in its source code. There, the securities key is mentioned, yet is is never actually used for the request. This means that manually specifying it in the third parameter of this.myExternalService.execute will change nothing.
I'll not accept this answer yet and I'm looking forward to finding a more Loopback-like approach.
I configured my service like this, to inject the basic authentication.
import {inject, lifeCycleObserver, LifeCycleObserver} from '#loopback/core';
import {juggler} from '#loopback/repository';
const SwaggerClient = require('swagger-client');
const config = {
name: 'jira',
connector: 'openapi',
spec: 'swagger-v2.json',
validate: false,
httpClient: (request: any) => {
request.headers["Authorization"] = "Basic " + Buffer.from("test:test").toString('base64');
return SwaggerClient.http(request);
},
};
#lifeCycleObserver('datasource')
export class JiraDataSource extends juggler.DataSource
implements LifeCycleObserver {
static dataSourceName = 'jira';
static readonly defaultConfig = config;
constructor(
#inject('datasources.config.jira', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
}
}

Regex for route work incorrect and return error 404

I have custom regex for match correct username:
^(?=.{5,20}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$
See demo
Here you can see my routes list:
Route::middleware(['userActivity'])->group(function () {
Route::group(['prefix' => '{nickname}','where' => ['nickname' => '^(?=.{5,20}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$']], function ($nickname) {
Route::name('user.')->namespace('User')->group(function () {
Route::middleware(['auth', 'company'])->group(function () {
Route::namespace('Vacancy')->group( function () {
Route::prefix('vacancy')->name('vacancy.')->group( function () {
Route::get('/manage', "VacancyController#manage")->name('manage');
Route::post('/save', "VacancyController#save")->name('save');
});
});
});
});
});
});
In this case when I go to route user.vacancy.manage:
http://website.com/user_1544080981/vacancy/manage
Return error:
404 Page Not Found
When I change my regex to:
^(?=.{5,30}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$
Note: Changed in regex only min and max length from {5,20} to {5,30}
Generaly when I see to part of url after domain name url length == 30
user_1544081143/vacancy/manage
But regex must work only for user nickname instead of to part url without domain name. Where I have any errors in my routes?
Regular expression, this is a better way then above in my opinion.
Route::any('{all}', function(){
return 'It Works';
})->where('all', '.*');
Using the Route::fallback method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you may define the fallback route within your routes/web.php file, all middleware in the web middleware group will apply to the route. Of course, you are free to add additional middleware to this route as needed:
Route::fallback(function () {
//
});

Pretender intercepted GET ... but no handler was defined for this type of request for an external request

I use Stripe in an Ember app. Stripe makes request to this address : https://checkout.stripe.com/api/outer/manhattan?key=... In my acceptance test, I have this message : Pretender intercepted GET https://checkout.stripe.com/api/outer/manhattan?key=... but no handler was defined for this type of request.
I tried to stub this request like this :
var server = new Pretender(function() {
this.get("/api/outer/manhattan", function() {
return [200, {}, this.passthrough];
});
});
But it does not work. I also tried with the full url or with a wildcard without success.
Is there a solution ?

ember-cli-mirage testing request params

I have default params that are added to the search request from a route. I would like to test these in ember-cli-mirage but am stuck on how to capture the request or requestBody so that I can assert against it.
Was looking for something similar to what I found on this SO post, but need access to the actual request and not the DOM. I am able to access the search params entered by the user (the 'text' param in my example) using currentUrl(), but the default params included in the request sent to the server, but not the url.
Is there a way to capture and assert against the request itself using ember-cli-mirage?
Something like
test('it appends default params to request'), function(assert) {
let searchUrl = '/my/route/url';
server.get(searchUrl, (db, request) => {
assert.equal(request.requestBody, "text=abc&all=true");
}
});
EDIT
I was able to get the test to pass using Qunit's async helper, like so:
test('it appends default params to athlete request', function(assert) {
assert.expect(2);
let done = assert.async();
server.get('/athletes', (db, request) => {
let params = request.queryParams;
assert.equal(params["page"], "1");
assert.equal(params["per"], "50");
done();
});
server.create('athlete', {first_name: 'John'});
visit('/athletes');
});
Still getting an error in the console for this test related to the json:api serialization:
normalizeResponse must return a valid JSON API document:
* meta must be an object
Going to open another question related to this failure elsewhere and link it in the comments.
The request param passed to your route handlers is the PretenderJS request object, which has some useful keys:
request.params, the dynamic segments of your route
request.queryParams, deserialized query request params
request.requestBody, the text body, You can use JSON.parse(request.requestBody) to turn this into an object.
So, if you wanted to assert against the query params, use request.queryParms.

Remove unnecessary URL parts/parameters

I've got a blog with article-urls like this http://ali.dj/blog/unlimited-fps-in-unity-editor
My old blog had URLs like this: http://ali.dj/blog/unlimited-fps-in-unity-editor/comment-page1 etc.
When the article is valid but everything following it is unnecessary I would like to strip out parts and parameters after the URL.
This http://ali.dj/blog/unlimited-fps-in-unity-editor/comment-page1
or this http://ali.dj/blog/unlimited-fps-in-unity-editor/comment-page1?ref=foo
becomes this http://ali.dj/blog/unlimited-fps-in-unity-editor
Currently I've got a catch-all route which will redirect the user to the error-page
How can I strip out unnecessary parts?
Here's the router.js I currently use for articles:
this.route('blog', { 'path' : 'blog/'}, function() {
this.route('post', { 'path' : ':permalink' })
this.route('not-found', { 'path' : 'not-found'})
});
// Error routes
this.route('not-found', { path : '/not-found'})
this.route('catch-all', { path : '/*path'}) // redirects to not-found