datatables.net ajax outside angular scope does not call the interceptor - datatables

Angular CLI: 7.3.3 | Node: 10.7.0 | OS: linux x64 | Angular: 7.2.6
I need server side pagination, sorting and filtering, my table will get 100.000 records easily.
Project example -> https://github.com/sibelly/angular-with-datatablesnet
This example consumes themoviedb API, it's just for testing, that is not my real scenario. Here in this project, I don't activate the server side option as I said is just for testing the calling to my interceptor, just to prove that with ajax directly the interceptor isn't called.
On my real scenario, I have a Laravel API, which has the methods that expect the pagination, sorting and filtering params, because I need it to be done server side to reach a better performance and usability.
When I call the API endpoint using HttpClient, it calls my error.interceptor.ts normally, but the pagination, sorting and filtering information isn't passing to the URL.
So, I concluded that I need to call it via Ajax when I call using the ajax the params are sent as expected.
But what happens is that the user token expire and I need to redirect to the login page again. And this is done using the interceptors of angular, that is not activated when calling using ajax directly because it is outside angular's scope.
To resume, how can I intercept my ajax calling from datatables.net framework in my angular project?
Or is there another way to make the server side pagination, sorting and filtering?
movie.component.ts
ngOnInit() {
//When calling this method the error.interceptor is activated normally
//this.getMostPopularMovies();
//But when I call direct using the AJAX, the interceptor doesn't work
this.dtOptions = {
ajax: {
url: 'https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=2ed54a614803785fce2d7fe401cc3b21',
params: {
api_key: this.moviedbService.apiKey
},
dataSrc: 'results'
//type: 'GET',
//headers: {"Authorization": 'Bearer ' + JSON.parse(localStorage.getItem('authUser'))["token"]}
},
columns: [
{ data: 'title' },
{ data: 'release_date' }
]
};
}
getMostPopularMovies(){
this.moviedbService.getMostPopularMovies().
subscribe((movies: any) => {
this.moviesList = movies.results;
console.log("====", movies);
}, (error: any) => {
console.error("Erro-> ", error);
});
}
Thanks, guys o/

Related

Playwright: unable to login via API setting cookie (able to do it with Cypress)

I'm trying to implemented login via API following Playwright's guidelines but somehow nothing seems to be working.
As a comparison I've built the same in Cypress and it works out of the box:
Context:
Playwright Version: 1.30
Operating System: Mac
Node.js version: v16.19.0
Browser: Chromium
I am unable to make a simple API login that works perfectly using Cypress instead. Let me share the 2 code snippets for comparison:
Simple test case:
API request to the login end-point - Auth token is retrieved
set the auth token as a cookie
navigate to a page that is accessible only if authenticated
Code Snippet
Cypress (working fine)
const body = {
username: 'username...',
password: 'password',
rememberMe: true,
};
describe('Login via API to management console', () => {
it('Login via API to management console', () => {
cy.request({
method: 'POST',
url: loginEndPoint,
headers: {
'Content-Type': 'application/json',
},
body,
}).then((response) => {
cy.setCookie('Authorization', `Token ${response.body.data.token}`);
});
cy.visit(`/management`);
});
});
Playwright (not working)
test('Login via API', async ({ browser }) => {
const context = await browser.newContext();
const page = await context.newPage();
const loginResponse = await context.request.post(`https://${process.env.MANAGEMENT_URL}/web/api/v2.1/users/login`, {
data: {
username: process.env.MANAGEMENT_USER,
password: process.env.MANAGEMENT_PASSWORD,
rememberMe: true,
}
});
const {
data: { token },
} = await loginResponse.body().then((b) => {
return JSON.parse(b.toString());
});
expect(token).toMatch(/^[a-z0-9]{80}$/)
await context.addCookies([{ name: 'Authorization', value: `Token ${token}`, path: '/', domain: `https://${process.env.MANAGEMENT_URL}` }]);
await page.goto(`https://${process.env.MANAGEMENT_URL}/management/`);
await expect(page).toHaveURL(/management/);
});
Describe the bug
Both scripts are successful at retrieving the authentication token but somehow either I'm doing something wrong with setting the cookie in Playwright or there is an issue. I'd assume the 2 scripts should be comparable.
Furthermore: I've tried to execute login via UI using global-setup, saving the storage-state, loading it before running the test and it fails also in this case... so there is something that is not setting properly the state in this case or the cookie in the previous one.
Not entirely sure why the cookie approach wasn’t working, perhaps the https:// part should be removed from the domain?
That being said, in Playwright you shouldn’t even need to do that especially within a single test, looking at the Playwright docs on signing in via the API and related page about the request context particularly under cookie management. The associated request and browser contexts share cookies, so once you complete the login request, the browser should already have the cookie state too and be logged in, so you should be able to just remove getting the token and adding the cookie. Or you can login with the API in the global setup even, as that doc showed. Just make sure in that case to save the storage state, and specify the same file in your config.
I see you tried the global setup approach (through the UI, but you can use the API since you have it), not sure what happened there. I would say to ensure that you specified the storageState in the config; I would be curious how you loaded it as mentioned, and if you’re still having problems maybe share the code you’re using for that piece?
Hope that helps or we can troubleshoot further!

Inertia Plain Axios Request

Its a very normally requirement that a web page may have multiple API requests.
I have been Inertia.js and this works very good but it lacks a very important feature to make plain XHR requests.
However Inertia.js provides Inertia.reload() but it some cases it's not very useful.
I am looking for something like Inertia::xhr() keeping in mind that I can still use Inertia.js interceptors like onStart, onFinish etc.
Anyone can help to achieve this?
You can do plain XHR requests with Axios (which is already a dependency anyway), or alternatively use the native Fetch api.
From the docs:
Using Inertia to submit forms works great for the vast majority of situations. However, in the event that you need more fine-grain control over the form submission, there's nothing stopping you from making plain xhr or fetch requests instead. Using both approaches in the same application is totally fine!
See also here
If you do a Interia::visists it will cause a full page reload.
If you want to prevent that making a call with axios/fetch makes sense. For example, if you just want to add a comment functionality, you may not want to reload the entire page. If the page is slow, the user will feel the delay when adding the comment.
Only thing you have to take care is CSRF token. If you used axios, it should read the XSRF-TOKEN from cookie automatically.
If you use the fetch api, you would need to manually provide the CRSF token.
Meaning you have to either read it from shared data or from the cookie.
If you add it to shared data, it would look like this:
public function share(Request $request): array
{
$isAdmin = Auth::user() instanceof Admin;
return array_merge(parent::share($request), [
'csrf' => csrf_token(),
'session' => [
'notification' => session('notification'),
],
'auth' => self::share_auth(Auth::user()),
]);
}
and your fetch request would look like this:
fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': usePage().props.value.csrf
// 'Content-Type': 'application/x-www-form-urlencoded',
},
body: JSON.stringify({ name: name}) // body data type must match "Content-Type" header
});
Intertia doesn't support plain XHR request.
But we have written a package that provides very similar APIs as Inertia.
https://github.com/JoBinsJP/formjs

Using Cypress to Test an App That Relies on OAuth

I've inherited a Node.js web app that uses relies on OAuth. Whenever you visit a page the app ensures you've authenticated. Please note, there no Angular, React, Vue, etc here. Each page is straight up HTML.
I want to test this site using Cypress. My problem is, I'm stuck on the initial redirect from the auth provider. Cypress acknowledge OAuth is a challenge.
commands.js
Cypress.Commands.add('login', (credentials) => {
var settings = {
'clientId':'<id>',
'scope':'<scope-list>',
...
};
var body = `client_id=${settings.clientId}&scope=${settings.scope}...`;
var requestOptions = {
method: 'POST',
url: 'https://login.microsoftonline.com/...',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body
}
cy.request(requestOptions);
});
Then, in my test, I have:
context('Home', () => {
it('Visits Successfully', () => {
cy.login();
cy.title().should('include', 'welcome');
});
});
In the test runner, I see the login POST request is occurring. I confirmed that an access token is being received using a console.log, however, my title is empty. It's like the redirect after OAuth isn't happening in Cypress. However, when I visit the site in the browser, the redirect is happening as expected.
What am I missing?
What you might be missing is confusing between the actual UI flow and the programmatic flow of doing OAuth with a 3rd party website.
What you would want to do is to complete the programmatic login and then send the required parameters to your OAuth callback URL for your app manually in the test code.
an example is given here (though it uses a different grant type it gives you an idea) https://auth0.com/blog/end-to-end-testing-with-cypress-and-auth0/#Writing-tests-using-Cypress-Login-Command
another issue on the cypress github that deals with a similar problem
https://github.com/cypress-io/cypress/issues/2085
this also might help:
https://github.com/cypress-io/cypress-example-recipes/blob/master/examples/logging-in__single-sign-on/cypress/integration/logging-in-single-sign-on-spec.js

vue server side rendering and data population

Im currently refactoring an app and converting all my base code into vue. One of my requirements is to do server side rendering.
I have been follow vue ssr example along with hacker news example to help me understand ssr.
I do have however a question for which I cant find any good answer, and before further development, I want to make sure we are doing the right thing.
I want to know if its a good practice to have some actions in a vue store calling an api for server side rendering.
All the examples I have found deal with a real external endpoint they connect and perform request. But that is not the setup we have.
We do have a "normal" express app with its own endpoints; so, for example, express router looks a bit like this:
// This is our post model, it lives on the same app, so its not a external API
app.get('/posts', (req, res) => Posts.getPosts());
// Resolve request for SSR rendering, pretty much the same as in [vue ssr example](https://ssr.vuejs.org/guide/routing.html#routing-with-vue-router)
app.get(
const context = { url: req.url }
createApp(context).then(app => {
renderer.renderToString(app, (err, html) => {
if (err) {
if (err.code === 404) {
res.status(404).end('Page not found')
} else {
res.status(500).end('Internal Server Error')
}
} else {
res.end(html)
}
})
})
);
This part works fine on both client and server. If you request something to /posts you get your response back.
To have a more modular code, we are using vuex stores; being one of the actions called fetchPosts and this action is the responsible of fetching the current posts in the view.
...
actions: {
fetchPosts({ commit }) {
return $axios.get('/posts').then((response) => {
commit('setPosts', {
posts: response.data
});
});
}
},
...
I believe for client side this is good, but when rendering on the server, this is probably not optimal.
Reason being axios performing an actual http request, which will also have to implement auth mechanism, and in general, really poor performant.
My question is: is this the recommended and standard way of doing this ?
What are other possibilities that works in server and client ?
Do people actually creates separated apps for api and rendering app ?
Thanks !
I know this is an old question, but for future visitors:
The recommended way is to leverage webpack module aliases to load a server side api for the server and a client side api for the browser. You have to share the same call signature which allows the api to be "swapped".
This of course greatly improves performance as the server side api can do direct db queries instead fetching data over http.
In essence your webpack.server.config.js should have:
resolve: {
alias: {
'create-api': './create-api-server.js'
}
}
In your webpack.client.config.js:
resolve: {
alias: {
'create-api': './create-api-client.js'
}
}
Importing create-api will now load the required api.
Have a look at https://github.com/vuejs/vue-hackernews-2.0 to see a full example.

How to render HTML to the user of a step function endpoint?

I'm using serverless and https://github.com/horike37/serverless-step-functions to try and implement a system that is hit by a user, returns HTML based on a database entry for the params provided and then moves to a second function that writes to the database (without forcing the user to wait).
I think a step function in the right approach but I can't seem to get it to return HTML - it always returns a JSON body with the executionArn and startDate. e.g.
{
"executionArn": "arn:aws:states:us-west-2:.......etc...",
"startDate": 1513831673.779
}
Is it possible to have my html body return? At the moment my lambda function returns a simple h1 tag:
'use strict';
module.exports.requestHandler = (event, context, callback) => {
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html'
},
body: `<h1>Success!</h1>`,
};
callback(null, response);
};
This is the state machine I'm aiming to create.
I would suggest going for a react/angular/vue frontend hosted e.g. on S3/CDN that uses serverless for backend queries only, instead of rendering dynamic HTML through Lambdas. The 'standard' approach allows you to build apps that are much more responsive and can benefit from e.g. CDNs.
See e.g. https://www.slideshare.net/mitocgroup/serverless-microservices-real-life-story-of-a-web-app-that-uses-angularjs-aws-lambda-and-more or https://serverless-stack.com/