Using Ember.JS store to query Laravel 8 REST API - ember.js

I'm developing a personal trading card DB in Ember.JS with a Laravel 8 REST API... for fun? Currently working on a page to add cards to my DB in bulk. The user will select what deck they want to add cards to and the respective deck's card checklist will be displayed, where I can select what cards will be added. I'm stuck on displaying the appropriate card's for their respective deck.
I'm using the ember package Service
import { inject as service } from '#ember/service';
...
#service store;
Inside my controller, I'm calling an action that will update the variable/displayed cards.
#action updateModel() {
let x = this.store.query('card', {
year: {
eq : yearVar
}
});
...
}
This is the error info:
Uncaught (in promise)
Object { message: "Aborted",
name: "AbortError",
stack: "exports.DOMException#http://10.0.0.0:4200/assets/vendor.js:49690:28\nfetch/WHATWGFetch</xhr.onabort/<#http://10.0.0.0:4200/assets/vendor.js:49730:22\nsetTimeout handler*fetch/WHATWGFetch</xhr.onabort#http://10.0.0.0:4200/assets/vendor.js:49729:23\nEventHandlerNonNull*fetch/WHATWGFetch<#http://10.0.0.0:4200/assets/vendor.js:49728:11\ninitializePromise#http://10.0.0.0:4200/assets/vendor.js:47175:15\nPromise#http://10.0.0.0:4200/assets/vendor.js:47631:35\nfetch#http://10.0.0.0:4200/assets/vendor.js:49697:16\nexports.default#http://10.0.0.0:4200/assets/vendor.js:49801:30\n_fetchRequest#http://10.0.0.0:4200/assets/vendor.js:52788:16\najax#http://10.0.0.0:4200/assets/vendor.js:52753:35\nquery#http://10.0.0.0:4200/assets/vendor.js:52361:19\n_query/promise<#http://10.0.0.0:4200/assets/vendor.js:71800:61\ninvokeCallback#http://10.0.0.0:4200/assets/vendor.js:47150:17\nthen/<#http://10.0.0.0:4200/assets/vendor.js:47207:40\n#http://10.0.0.0:4200/assets/vendor.js:42711:62\ninvoke#http://10.0.0.0:4200/assets/vendor.js:41296:16\nflush#http://10.0.0.0:4200/assets/vendor.js:41211:19\nflush#http://10.0.0.0:4200/assets/vendor.js:41374:21\n_end#http://10.0.0.0:4200/assets/vendor.js:41815:34\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_ensureInstance#http://10.0.0.0:4200/assets/vendor.js:41972:14\nschedule#http://10.0.0.0:4200/assets/vendor.js:41665:19\n#http://10.0.0.0:4200/assets/vendor.js:42711:28\nthen#http://10.0.0.0:4200/assets/vendor.js:47207:14\n_query#http://10.0.0.0:4200/assets/vendor.js:71800:42\nquery#http://10.0.0.0:4200/assets/vendor.js:72905:32\nupdateModel#http://10.0.0.0:4200/assets/cam-card-comp.js:607:18\nEventListener.handleEvent*addEventListener#http://10.0.0.0:4200/assets/vendor.js:39057:15\ninstall#http://10.0.0.0:4200/assets/vendor.js:39194:23\ncommit/tag<#http://10.0.0.0:4200/assets/vendor.js:36926:25\ntrack#http://10.0.0.0:4200/assets/vendor.js:40488:7\ncommit#http://10.0.0.0:4200/assets/vendor.js:36924:42\ncommit#http://10.0.0.0:4200/assets/vendor.js:36999:19\ninTransaction#http://10.0.0.0:4200/assets/vendor.js:37019:13\n_renderRoots#http://10.0.0.0:4200/assets/vendor.js:6446:36\n_renderRootsTransaction#http://10.0.0.0:4200/assets/vendor.js:6490:14\n_renderRoot#http://10.0.0.0:4200/assets/vendor.js:6435:12\n_appendDefinition#http://10.0.0.0:4200/assets/vendor.js:6360:12\nappendOutletView#http://10.0.0.0:4200/assets/vendor.js:6350:12\ninvoke#http://10.0.0.0:4200/assets/vendor.js:41298:16\nflush#http://10.0.0.0:4200/assets/vendor.js:41211:19\nflush#http://10.0.0.0:4200/assets/vendor.js:41374:21\n_end#http://10.0.0.0:4200/assets/vendor.js:41815:34\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_end#http://10.0.0.0:4200/assets/vendor.js:41821:18\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n_end#http://10.0.0.0:4200/assets/vendor.js:41821:18\nBackburner/this._boundAutorunEnd#http://10.0.0.0:4200/assets/vendor.js:41539:14\npromise callback*buildNext/<#http://10.0.0.0:4200/assets/vendor.js:41040:35\nflush#http://10.0.0.0:4200/assets/vendor.js:26782:5\n_scheduleAutorun#http://10.0.0.0:4200/assets/vendor.js:41981:14\n" }
vendor.js:41829:18
I checked the URL the ember app was sending to the API, it is as follows, and it's correct:
http:10.0.0.0:4000/cards?year[eq]=2022
Took this URL and pasted it into my browser, it successfully returned the appropriate checklist. This tells me I'm creating the query correctly and sending it correctly. Mozilla Firefox's inspector shows the network GET request was BLOCKED, and I'm given the EmberJS error.

if request is blocked it could be CORS problem (by default it accept only same origin but in you case fornt-end served on 4200 and api on 4000), in laravel 8 check config/cors.php and allow all origines * something like this <?phpreturn['paths'=>['api/*'],'allowed_methods'=>['*'],'allowed_origins'=>['*'],'allowed_origins_patterns'=>[],'allowed_headers'=>['*'],'exposed_headers'=>false,'max_age'=>false,'supports_credentials'=>false,];

Related

On what domain is my Javascript in my Office Add-In

We have an Outlook Add-In which runs in OWA.
The Manifest sits on https://company.ourdomain.com
The Javascript sits on https://company.ourdomain.com
The Custom Web Service we wrote in-house sits on https://company.ourdomain.com
When I make a call from within JavaScript in response to an Add-In Command, I use the format https://company.ourdomain.com/api/Controller/Action in the ajax call.
I end up getting one of those CORS errors (sometimes it's pre-flight, other times CORB). Why am I getting this if the Javascript is literally sitting on the same domain as the web service?
I'm assuming I'm authenticated since I've logged into my Outlook account.
What gives?
NOTE:
As an experiment I attempted a RESTful call by directly typing in the URL (No OWA involved). This caused the code to Authenticate against Azure AD. Then afterward I logged into OWA in the same browser session and everything worked fine. Do I actually need to authenticate within the Javascript even if the webservice I'm calling is in the same domain?
AJAX CALL WHICH GENERATES ERROR
Remember, it will work just fine after I've made a RESTful call by making a call to my web service directly from the Browser
var apiUri = '/api/People/ShowRecord';
$.ajax({
url: apiUri,
type: 'POST',
data: JSON.stringify(serviceRequest),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
}).done(function (response) {
if (!response.isError) {
// response to successful call
}
else {
// ...
}
}).fail(function (status) {
// some other response
}).always(function () {
console.log("Completed");
});
OBSERVATION
When I call the api from the Address Bar the code below is run. This code never gets invoked by Javascript
[assembly: OwinStartup(typeof(EEWService.AuthStartup))]
namespace EEWService
{
public partial class AuthStartup
{
public void Configuration(IAppBuilder app)
{ app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Notifications = new WsFederationAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = "ourdomain.com";
return Task.FromResult(0);
}
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
Wtrealm = ConfigurationManager.AppSettings["ida:Audience"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new string[] { $"spn:{ConfigurationManager.AppSettings["ida:Audience"]}" }
}
});
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
MetadataAddress = ConfigurationManager.AppSettings["ida:MetadataAddress"],
});
}
}
}
There are a few problems with this I think.
The first one is you are trying to serve your static content off the same server you are serving your code from. This is in general considered a bad-practice, purely because no point in wasting those precious server resources for static content. Ideally you should upload your static content to a CDN - and let the users' browser make a request to some super-cached file server. However - I understand this option might not be available to you as of now. This also isn't the root cause.
The second and the real problem is, (you think you are but) you are not authenticated. Authentication in Outlook web-addins doesn't come by default, it's something you need to handle. When Outlook loads your web add-in into the side panel it makes certain methods available to you which you can use and kind-of create a pseudo-identity (as an example Office.context.mailbox.userProfile.emailAddress ) - but if you want real authentication, you will need to do that yourself.
There are three ways of doing that as far as I can tell.
The first one is through the Exchange Identity Token
Second one is through the Single Sign On feature
The third one - which I think is the most convenient and the simplest in logic to implement is using WebSockets. (SignalR might be what you need).
When the user loads your first page, make sure a JS value like window.Unique_ID available to them. This will come in handy.
Have a button in your UI - which reads "Authenticate"
When the user clicks to this button, you pop them out to a url which will redirect to your authentication URL. (Something like https://company.ourdomain.com/redirToAuth). This would save you the trouble of getting blocked in the side-panel, because you are using window.open with a url that's on your domain. Pass that Unique_ID to redirection which then redirects you to OAuth login URL. That should look like https://login.microsoftonline.com/......&state=Unique_ID
Right after popping the user to sign in window, in your main JS (which is client-side), you open a web-socket to your server, again with that Unique_ID and start listening.
When the user completes authentication, the OAuth flow should post back either an access token, or the code. If you get the access token, you can send it through the sockets to front-end (using the Unique_ID which is in the parameters of post-back) or if you had the code, you finish authenticating the user with a server-to-server call and pass the access token the same way afterwards. So you use that unique Id to track the socket that user connected from and relay access token to only that user.

Updating component display from route

To learn more about using services and components, I'm trying to set up a simple flash-message style service. Within my route, I'm saving a record and receiving either a json notice or json error from the server.
Since this is a behavior I'd eventually like to use app-wide, I'm using a simple service (injected into my routes) to handle displaying the message. Within a route, I'm able to call this.get('notification').displayMessage(msg).
Currently the displayMessage function within the service is just alerting the message because I'm stuck on how to create a component that the service can "update". How can a service communicate with a component so that I can send a message and display it from the component template?
profile/index route
user.save().then( (response) => {
//display response.notice in the app-notification component template
}, (response) => {
let errors = JSON.parse(response.errors);
//display response.error in the app-notification component template
this.get('notification').displayMessage(errors[0]);
}
service
import Ember from 'ember';
const { Service } = Ember;
export default Service.extend({
displayMessage(msg) {
alert("message ---> " + msg);
}
});
component
???
component template
<h2 class="alert">{{message}}</h2>
application template
{{app-notification message=message}}
In terms of accessing the service, I like the example that they using the docs: http://guides.emberjs.com/v2.1.0/applications/services/
The ShoppingCart service is injected into components that need to access the Cart.
Once you have access to the ShoppingCart you should be able to:
set a Notification in the service for User from your route error catch.
set up a Computed Property based on the Notification service (some sort of hasNotification method) on the Component hasError that can toggle the error message.
As soon as you set a notification your component will see that the property it is watching has changed and will show/hide your notification.

Google Analytics ID stored in cookie is undefined

We are installing google analytics via Google Tag Manager.
We have custom variable that supposed to take the GA customer id, and send it to our GA.
The variable is defined as follows:
function() {
try {
var cookie = {{GA_ID_Cookie}}.split(".");
return cookie[2] + "." + cookie[3];
} catch(e) {
return 'N/A';
}
}
While {{GA_ID_Cookie}} is a first party cookie variable named "_ga".
In most cases, this values works, but there are some cases where GA_ID_Cookie is undefined (and exception is thrown).
It happens in all browsers. There enough users with "N/A", so its not about cookies disabled issue.
The GTM installs the GA on page view event; It uses this problematic variable as a custom dimension.
My question is how come the ga id is null, and how can we overcome this problem and get the id in other ways.
It is likely that your tag is fired before the cookie is generated.
Try to change the page view to window load. Clear the cookie and retry, it should work.
Like Ashley pointed out you might be facing a race condition whereby you try to access the cookie before it is set by GA.
Please note that the GA cookie ID contains some uninteresting info from the point of view of identifying users, namely the version which should be removed.
If your GA cookie looks like this:
_ga=GA1.2.1033501218.1368477899;
Then the part you're interested in is:
1033501218.1368477899
To retrieve the client ID via the browser, the official way is as follows:
https://developers.google.com/analytics/devguides/collection/analyticsjs/accessing-trackers
// Initializing the `ga` command queue so that commands
// can be queued even if the GA snippet is not loaded
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
// Queuing a command to retrieve the Client ID when the tracker is ready
ga(function(tracker) {
// Logs the client ID for the current user.
console.log(tracker.get('clientId'));
});
If you are using GTM then you need to create a task:
https://www.simoahava.com/analytics/13-useful-custom-dimensions-for-google-analytics/#13-client-id
function() {
return function(model) {
return model.get('clientId');
};
}
If you want to retrieve the Client ID via the server, then you simply need to parse the cookie HTTP header (below example is from request to stackoverflow website) using an HTTP library of your choice and getting rid of the leading GA\d\.\d\. pattern which represents the cookie version.
cookie: prov=f67bae3b-f99c-2f22-84fc-7c2a62862f3d; _ga=GA1.2.1380536973.1571212618; ...

EmberJS and WebSocket | Best Approach?

I have a again which I can't answer for my self properly, maybe because of my lack in expierience with EmberJS.
I have to develop a management interface in EmberJS, using Symfony 2 for the backend, which should act and feel like a desktop application. So far so good, but since alot of people will work with the data inside this application, i would really like to use a WebSocket adapter implementation for EmberJS, since every connected client should always know about changes in entities immediately (or asap). I could write a WebSocket adapter for EmberJS but my problem here is that the WebSocket will do much more then RESTful operations, also the server will send messages without any EmberJS request (e.g. an entity changed and the server broadcasting this change to all clients). That means that i need a "command" structure on top of RESTful operations which, as far as my expierience goes, will not work with a pure DS Adapter.
For example:
Maybe i will trigger a controller method that will send a websocket message like this:
{command: "say", parameters: {message: "Hello guys!"} }
This command is not Entity (DS) related and will never go into the application store.
Another example would be like this:
{command: "load entity", parameters: {type: "Vendor\Bundle\Entity\Type", id: 43} }
Which would load an entity which should be stored in the application store.
Well, as i said, im not that familiar with EmberJS that I could figure out which the best approach could be. Should I bypass the DS Adapter completely and check for "isDirty" and just the push methods after loading entities? I'm happy about any idea you have!
As far as I understand your question, you want to push changes from your backend to your single page app?
You can push custom JSON into your application's store in Ember by using self.store.push('modelName', json). Have a look at the docs for a better undestanding.
So for example if your server sends you JSON via websocket that looks like this
{
- "message": {
"type": "fooModel",
"data": {
... // Model attributes here
}
}
}
you can push the data into your store. The following snippet would work with SocketIO for example:
App.ApplicationRoute = Ember.Route.extend({
activate: function() {
// connect to the websocket once we enter the application route
var socket = window.io.connect('http://localhost:8080');
var self = this;
socket.on('message', function(data){
self.store.push(data.type, data.item);
});
}
});
You can easily modify this snippet to fit your needs.

what is the role of router in a single page application

I am new to Ember-js, I was recently going through some blog entries and also saw the video of Ember-js introduction by Tom dale.
to summarize they say that Router Api is newly introduced and it the best thing that happened to Ember-js,Router Api is used to manage the state of the application and each state is identified with a URL, now for a single page application where in we use only one URL, what is the role of the router, will there be only one routeing entry which is mapped to '/'(index)? If yes, then we lose the advantage provided by the Router api right?
now for a single page application where in we use only one URL, what is the role of the router, will there be only one routeing entry which is mapped to '/'(index)?
Typically a single page application will still use the url. For example watch url change when using gmail. So in this case single page application means the browser doesn't fetch a new page as url changes. Like gmail, a typical ember single-page application will change url as user navigates to various parts of the application. The ember router takes care of this automatically.
If yes, then we lose the advantage provided by the Router api right?
If you decide not to use the url, and really want it to just stay "/" the whole time, you can still use the router. Just set the router's location type to "none"
See http://emberjs.com/guides/routing/specifying-the-location-api/
I understand that routing here means managing states, but at any point of time user can be in a set of states for instance take gmail the user would be in login state and compose state, how to manages multiple states existing together?
For sure that is true. The ember router is basically a statechart where routes (leaf nodes) are nested under some number of resources. So in the case of gmail for example only a logged in user can be in the compose state.
GMail URL: https://mail.google.com/mail/u/0/?shva=1#inbox
// Gmail Routes:
* /mail - the mail application
* /u/0 - connected account index 0 for the current user
* ?shva=1 - http://stackoverflow.com/questions/1692968/what-is-shva-in-gmails-url
* inbox - folder name
EmberMail Version: https://mail.ember.com/mail/u/0/inbox
// EmberMail Routes
this.resource('mail', { path: '/mail' }, function() {
this.resource('currentUser', { path: '/u' }, function() {
this.resource('account', { path: '/:account_id' }, function() {
this.route('folder', { path: '/:folder_id' });
});
});
});
can you point me to a example application which uses routing extensively?
The best example I know of is discourse. Check out the following for example of how a large ember application uses the ember router:
Discourse Application routes
Discourse Admin routes