Request from Ember front to Rails back is not happening - ruby-on-rails

I am implementing a front-end in ember 1.13 with a Rails back-end and having the following problem:
After the user is authenticated, I don't seem to be able to retrieve the user's record from the back-end. The browser debugger does not even show a request being made. This is code:
// app/services/session-user.js
import Ember from 'ember';
const { inject: { service }, RSVP } = Ember;
export default Ember.Service.extend({
session: service('session'),
store: service(),
loadCurrentUser() {
currentUser: {
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return this.get('store').findAll('user', userId);
}
}
}
});
There is a login controller which handles the authentication. But the code for getting the data is in the applications's route:
// app/routes/application.js
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
const { service } = Ember.inject;
export default Ember.Route.extend(ApplicationRouteMixin, {
sessionUser: service('session-user'),
beforeModel() {
if (this.session.isAuthenticated) {
return this._loadCurrentUser();
}
},
sessionAuthenticated() {
this._loadCurrentUser();
},
_loadCurrentUser() {
return this.get('sessionUser').loadCurrentUser();
},
});
For extra measure I am defining the session store:
// app/session-stores/application.js
import Adaptive from 'ember-simple-auth/session-stores/adaptive';
export default Adaptive.extend();
If there are files I should post, please let me know.
Any hints will be highly appreciated as I am rather new to ember. I have spent several hours researching without luck, as things seem to have changed quite a lot throughout versions.

Look at your service code.
var userId = this.get('user_id');
if (!Ember.isEmpty(userId)) {
return this.get('store').findAll('user', userId);
}
I don't see code that you provided in question where you setting up user_id variable. So if user_id not defined then if statement won't get executed because of !.

Related

Next.js with SSR: How to access cookies to retrieve a JWT and then make a post request with it

Explanation of my problem
So I am currently on a project of my traineeship that requires me to make a small app using Redux to manage the state of the app itself and TanStack Query (a.k.a. React Query v4) to manage asynchronous data from API calls
I am currently using Next.js with Server-Side Rendering (SSR)
And I have a big issue at the moment with one of the pages
So when the user logs in, they're redirected to their profile page, and I want to make a post request with the JSON Web Token stored in cookies to retrieve their info such as their first name and last name
What I did
I used the Next.js function getServerSideProps to make a POST request with the JWT, but because my app uses SSR. I do not have access to cookies and the JWT is stored inside cookies, but I cannot have access to client-side information until the component is fully mounted, also the app crashes when reloading because of that
I used the native React hook useEffect, I declared the variable with the value of the JWT since now I have access to the cookies, but now I cannot use the TanStack Query hook useMutation because you cannot use hooks inside other hooks
(Current implementation) I used a similar approach, but this time declared the JWT variable as undefined along with the constant using the useMutation outside the useEffect and added the JWT variable inside the array of dependencies of the hook, then I retrieved changed the value of the variable to contain the JWT and made a POST request but the post request fails
Here's the code of my current implementation
//React
import { useEffect, useState } from "react";
//Next
import Head from "next/head";
import { NextRouter, useRouter } from "next/router";
//Components
import AccountCard from "#/components/AccountCard/AccountCard";
import Button from "../../components/Button/Button";
//Utils
import { log } from "../../utils/functions/helper-functions";
import { savingsData } from "../../utils/variables/savings-data";
import CookieService from "#/utils/services/cookies.service";
import ApiService from "#/utils/services/api.service";
//Redux
//React-Redux
import { useSelector } from "react-redux";
import { useMutation } from "#tanstack/react-query";
//This is the page of the user
/**
* User page
*
* Route: `/profile/`
* */
export default function Profile(): JSX.Element {
// log({ jsonWebToken });
//We IMPORT the value of the logging state of the user when logging in
const userIsLoggedIn: boolean = useSelector((state: any) => {
return state.isLoggedIn;
});
//We're going to use the router hook to get the current to redirect the user
//if they're not logged in
const router: NextRouter = useRouter();
[...]
let jsonWebToken: string | undefined = undefined;
//We make the POST request
const apiService: ApiService = new ApiService();
const userProfileMutation = useMutation({
mutationFn: (jwt: string) => {
return apiService.postProfile(jwt);
},
onMutate: () => {},
onSuccess: (data, variables) => {
log("SUCCESS, USER INFOS:", data);
},
onError: () => {
log("FAILED TO RETRIEVE USER INFOS");
},
});
//We cannot use the push() method of the router to redirect the user to the sign-in page
//if the user isn't logged in because of the SSR (push is client side only)
useEffect(() => {
//If the user isn't logged in we redirect them to the sign-in page
const userIsNotLoggedIn: boolean = !userIsLoggedIn;
if (userIsNotLoggedIn) {
router.push("/sign-in/");
}
//We recover the jwt inside the browser"s cookies
const cookieCreator: CookieService = new CookieService();
//We initialise it
jsonWebToken = cookieCreator.getCookieByName("jwt")?.value;
log(jsonWebToken);
//#ts-ignore
userProfileMutation.mutate(jsonWebToken);
}, [jsonWebToken]);
return (
<>...</>
);
}
So I'd be very grateful if somebody was able to help me with this issue

Disconnect and unsubscribe Action Cable on Rails 6

This was already asked here, but it seems that the old methods (example and example) no longer work on Rails 6 because you cannot use App.channel inside your app/javascript/channels/your_channel.js.
Though, correct me if I'm wrong because I'm new to Rails.
My question is: how to disconnect or/and unsubscribe from client side on Rails 6?
I'm especially interested in the "on page change" scenario.
The solution was to save the subscription on a this variable and do the remove call if this variable is not null when page loads, then do the new subscription.
// File: app/javascript/channels/your_channel.js
import consumer from "./consumer";
$(document).on('turbolinks:load', function() {
if (this.subscription) {
consumer.subscriptions.remove(this.subscription);
console.log("unsubing");
}
if ($('#hashtag').attr('data-hashtag-id')) {
var subscription = consumer.subscriptions.create({channel: "TweetsChannel", id: $('#hashtag').attr('data-hashtag-id')}, {
connected() {
console.log("we are live on: " + $('#hashtag').attr('data-hashtag-id'));
},
disconnected() {
console.log("disconecting")
},
received(data) {
$('#tweets-card').prepend(data.content)
}
});
this.subscription = subscription;
}
});
Based on what you shared with us, I imagine you are trying to remove a subscription. You can remove it using consumer.subscriptions.remove [1].
Could you share the snippet of code you have in your app/javascript/channels/your_channel.js?

ionic native storage does not work on iOS

I use Ionic 3 on one of my projects with an authentication system. I use native storage when the user wants to connect. It works on Android but on iOS, it redirects me to the login screen even using platform.ready (). I saw that several people were a similar problem but no answer, so I wanted to know if someone was facing the same problem and if he found a solution. Here is my code:
this.plt.ready().then(() => {
this.nativeStorage.setItem('userStorage', { stayConnected: (typeof this.stayConnected == "undefined" || this.stayConnected == false ? '' : 'stayConnected'), userId: (result as any).id, userLogin: (result as any).login })
.then(
() => {
this.loader.dismiss();
this.navCtrl.setRoot(HomePage);
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
)
},
error => {
this.loader.dismiss();
this.presentToast(this.languageLogin.error, 3000, "bottom");
});
thank you for your answers.
I would put 2 function storeUser() and getUser() into the same provider UserService like belows
Then add UserService to the constructor of any pages required.
It works for both IOS, Android and web
import {Storage} from '#ionic/storage';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class UserService {
constructor(private storage: Storage){}
public storeUser(userData): void {
this.storage.set('userData', userData);
}
public getUser(): Observable<any>
return Observable.fromPromise(this.storage.get('userData').then((val) => {
return !!val;
}));
}
Yes, I have faced issues while using ionic native storage plugins. So I turned to javascript Window localStorage Property and it's working completely fine.
Syntax for SAVING data to localStorage:
localStorage.setItem("key", "success");
Syntax for READING data from localStorage:
var lastname = localStorage.getItem("key");
Syntax for REMOVING data from localStorage:
localStorage.removeItem("key");
and now you can write your code with this property, like this -
if (lastname == "success"){
this.navCtrl.setRoot(HomePage);
} else{
alert("Not matched")
}
You are inside a platform.ready(), which is good. The storage package also has a .ready() that you may want to leverage, which specifically checks if storage itself is ready. If this runs at startup there is a decent chance storage is initializing.
Also, this starts to get into some crazy promise chaining messiness. I'd suggest diving into async/await. Something like the (untested) code below.
try{
await this.plt.ready();
await this.nativeStorage.ready();
let stayConnectedValue = (this.stayConnected) ? 'stayConnected' : '';
await this.nativeStorage.setItem('userStorage', { stayConnected: stayConnectedValue , userId: (result as any).id, userLogin: (result as any).login });
this.navCtrl.setRoot(HomePage);
}
catch(err){
this.presentToast(this.languageLogin.error, 3000, "bottom");
}
finally{
this.loader.dismiss();
}

Passing very large XML data as params in Angular 2 to Rails

I have a text area, where user can paste any XML. Then on click of a button, the XML is sent to the server to get the response. I find, this works perfectly with smaller XMLs. But, my issue is, when I send a large XML, the Rails server log simply says
!! Invalid
request
CODE
Provider.ts
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { HttpClientService } from './../services/httpclient.service';
#Injectable()
export class QuestWebServiceTestProvider {
constructor(private http: HttpClientService) {}
getResponseXML(params) {
return this.http.get('/api/v1/get_response_xml?request_xml=' + params["request_xml"]);
}
}
Component.ts (this passes the XML read from textarea to provider)
getResponse() {
let requestXml = this.requestXmlElement.nativeElement.value.trim();
if(requestXml.length > 0) {
let requestXmlJson = encodeURI(requestXml);
this.<provider>.getResponseXML({request_xml: requestXmlJson).subscribe(
data => {
console.log(data);
}
)
}
}
What is the best way to send larger XMLs?
Sorry, for the typo in the question. I actually use POST request.
Thanks Pavel Mikhailyuk for pointing it.
This is how I fixed it:
Instead of framing the query params manually as:
...get_response_xml?request_xml=' + params["request_xml"]
I pass the XML as options to HTTP POST:
let options = {request_xml: params["request_xml"] };
return this.http.post('...get_response_xml?, options);
This seemed to work !!!

How would I go about getting AJAX data *from* Rails and render it back in Rails?

The project I've been working on has taken many shapes over the last few months, but its most recent incarnation was one which involved a RESTful Rails backend with React, Jade and Express on the frontend. This worked fine since React and Jade handled any data rendering on the frontend, and it allowed us to break out the requests from one monolithic request to about three much, much tinier ones.
The mandate is now to merge the view components into Rails, and this...is where I'm stuck.
As I said before, the world was different - The RESTful Rails routes were on one side of the fence, and the client was on another, meaning that AJAX requests were the only game in town. Here are some sample routes:
v1/property/:field
v1/property2/:field
v1/property3/:field
All of these routes work independently of any UI, so for the purpose of this question, we should treat them as a data source.
These are all requested on a single dashboard-like page, with each request pertaining to a specific part of it, all backed by a search bar which allows one to insert a specific calendar date. I've got this visually working now in a single view (mind the polygons).
My main concern is the request to each of the three other parts of the view. It is unclear to me how to have each of these independently make their requests to the appropriate RESTful route with the appropriate value provided by the search bar.
I've tried a hackish approach to this particular problem:
Within the search module's JS file, on its submission, create a new event called doingSearch.
Use this event to provide the information I received from the search module's <input> tag, falling back to its placeholder value if I don't have anything else.
In _property.js.erb, I listen for this event and emit two AJAX requests:
The first request is to the endpoint, v1/property/:field, and if it is successful, the second request is a POST, which goes to a route /board/:field/handle_post. The belief here is that I have more control over this route, and can dictate what's rendered here.
I then render _property.html.erb at the end of _property.js.erb, but I would rather like to pass the JSON data into _property.html.erb as if it were a Rails model for simplicity's sake. If at all possible, the logical solution would be to render the view after the POST request had been submitted, but that's another sticking point - I'm unclear on how to approach that.
How would I go about accomplishing this?
If you are dealing with data you will want to use the flux pattern. Here is an example from https://github.com/calitek/ReactPatterns React.14/ReFluxSuperAgent. The api.store handles the actions that initiate an ajax request. The util/api.js handles the ajax requests. The basic.store receives the data through actions from util/api.js and passes the data on to the control component with events. The control component could then pass the relevant data on to other components as props. Those components can trigger events that are listened to by the api.store.
import Reflux from 'reflux';
import Actions from './Actions';
import ApiFct from './../utils/api.js';
let ApiStoreObject = {
newData: {
"React version": "0.14",
"Project": "ReFluxSuperAgent",
"currentDateTime": new Date().toLocaleString()
},
listenables: Actions,
apiInit() { ApiFct.setData(this.newData); },
apiInitDone() { ApiFct.getData(); },
apiSetData(data) { ApiFct.setData(data); }
}
const ApiStore = Reflux.createStore(ApiStoreObject);
export default ApiStore;
import request from 'superagent';
import Actions from '../flux/Actions';
let uri = 'http://localhost:3500';
module.exports = {
getData() { request.get(uri + '/routes/getData').end((err, res) => { this.gotData(res.body); }); },
gotData(data) { Actions.gotData1(data); Actions.gotData2(data); Actions.gotData3(data); },
setData(data) { request.post('/routes/setData').send(data).end((err, res) => { Actions.apiInitDone(); }) },
};
import Reflux from 'reflux';
import Actions from './Actions';
import AddonStore from './Addon.Store';
import MixinStoreObject from './Mixin.Store';
function _GotData(data) { this.data1 = data; BasicStore.trigger('data1'); }
let BasicStoreObject = {
init() { this.listenTo(AddonStore, this.onAddonTrigger); },
data1: {},
listenables: Actions,
mixins: [MixinStoreObject],
onGotData1: _GotData,
onAddonTrigger() { BasicStore.trigger('data2'); },
getData1() { return this.data1; },
getData2() { return AddonStore.data2; },
getData3() { return this.data3; }
}
const BasicStore = Reflux.createStore(BasicStoreObject);
export default BasicStore;
import React from 'react';
import BasicStore from './../flux/Basic.Store';
let AppCtrlSty = {
height: '100%',
padding: '0 10px 0 0'
}
const getState = () => {
return {
Data1: BasicStore.getData1(),
Data2: BasicStore.getData2(),
Data3: BasicStore.getData3()
};
};
class AppCtrlRender extends React.Component {
render() {
let data1 = JSON.stringify(this.state.Data1, null, 2);
let data2 = JSON.stringify(this.state.Data2, null, 2);
let data3 = JSON.stringify(this.state.Data3, null, 2);
return (
<div id='AppCtrlSty' style={AppCtrlSty}>
React 1.4 ReFlux with SuperAgent<br/><br/>
Data1: {data1}<br/><br/>
Data2: {data2}<br/><br/>
Data3: {data3}<br/><br/>
</div>
);
}
}
export default class AppCtrl extends AppCtrlRender {
constructor() {
super();
this.state = getState();
}
componentDidMount() { this.unsubscribe = BasicStore.listen(this.storeDidChange.bind(this)); }
componentWillUnmount() { this.unsubscribe(); }
storeDidChange(id) {
switch (id) {
case 'data1': this.setState({Data1: BasicStore.getData1()}); break;
case 'data2': this.setState({Data2: BasicStore.getData2()}); break;
case 'data3': this.setState({Data3: BasicStore.getData3()}); break;
default: this.setState(getState());
}
}
}

Resources