I am new to building websites and all I want to do at this stage is to use local JSON file to retrive data instead of mirage provided in ember tutorial. you have mirage/config.js like this:
export default function() {
this.namespace = '/api';
let rentals = [{
//JSON
}];
this.get('/rentals', function(db, request) {
if(request.queryParams.area !== undefined) {
let filteredRentals = rentals.filter(function(i) {
return i.attributes.area.toLowerCase().indexOf(request.queryParams.area.toLowerCase()) !== -1;
});
return { data: filteredRentals };
} else {
return { data: rentals };
}
});
// Find and return the provided rental from our rental list above
this.get('/rentals/:id', function (db, request) {
return { data: rentals.find((rental) => request.params.id === rental.id) };
});
}
This article shows part of the solution but I don't know where it's supposed to be written. Any help would be much appreciated.
There are a few different options for stubbing some data without using mirage. The clearest and easiest is fetch.
Put your json file in the public folder, let's call it something.json. Then, use fetch to get the data (this is the model hook of a route):
model() {
return fetch('something.json')
.then(function(res) {
return res.json()
})
}
This answer applies from at least 1.13 onward (and possibly earlier). It was written as of 3.1.
Related
I am looking at some older Postman test scripts, and since I don't have much experience, I need help understanding this a bit better.
The following are in the test scripts
//schema validation
const schema201PostSuccess = pm.collectionVariables.get("schema201PostSuccess");
const schema400BmcIdFail = pm.collectionVariables.get("schema400BmcIdFail");
const schema401Unauthorized = pm.collectionVariables.get("schema401Unauthorized");
const schema400BadRequest = pm.collectionVariables.get("schema400BadRequest");
const schema404InsufficientPermission = pm.collectionVariables.get("schema404InsufficientPermission");
const schema500InternalServerError = pm.collectionVariables.get("schema500InternalServerError");
pm.test("Wishlist Record Created Schema Validated", function() {
pm.response.to.have.jsonSchema(schema201PostSuccess);
});
pm.test("Incorrect bmc_id Schema Validated", function() {
pm.response.to.have.jsonSchema(schema400BmcIdFail);
});
pm.test("Unauthorized Schema Validated", function() {
pm.response.to.have.jsonSchema(schema401Unauthorized);
});
pm.test("400 Bad Request - Schema Validated", function() {
pm.response.to.have.jsonSchema(schema400BadRequest);
});
pm.test("404 Insufficient Permission - Schema Validated", function() {
pm.response.to.have.jsonSchema(schema404InsufficientPermission);
});
pm.test("500 Internal Server Error - Schema Validated", function() {
pm.response.to.have.jsonSchema(schema500InternalServerError);
});
Now, I'd like to put the tests into an if-else statement so rather than a bunch of fails and one pass, it just shows the correct result with the proper schema validated.
Here's how it currently looks Postman Test Result Screenshot
You can do something like this.
let status = pm.response.code;
if(status === 201){
pm.test("Wishlist Record Created Schema Validated", function() {
pm.response.to.have.jsonSchema(schema201PostSuccess);
});
} else if (status === 400) {
pm.test("Incorrect bmc_id Schema Validated", function() {
pm.response.to.have.jsonSchema(schema400BmcIdFail);
});
...
}
I am trying to pass data from the server to the client to load my app faster and prevent multiple calls to the database.
Via Fetch
SvelteKit is made to do this via the fetch function. This is great if you have an endpoint that allows for custom fetch. But what if you don't?
Firebase is a perfect example of not having a custom fetch function.
Cookies
I would think I could use cookies, but when I set the cookie, it just prints 'undefined' and never gets set.
<script lang="ts" context="module">
import Cookies from 'js-cookie';
import { browser } from '$app/env';
import { getResources } from '../modules/resource';
export async function load() {
if (browser) {
// working code would use JSON.parse
const c = Cookies.get('r');
return {
props: {
resources: c
}
};
} else {
// server
const r = await getResources();
// working code would use JSON.stringify
Cookies.set('resources', r);
// no cookies were set?
console.log(Cookies.get());
return {
props: {
resources: r
}
};
}
}
</script>
So my code loads correctly, then dissapears when the browser load function is loaded...
Surely there is a functioning way to do this?
J
So it seems the official answer by Rich Harris is to use and a rest api endpoint AND fetch.
routes/something.ts
import { getFirebaseDoc } from "../modules/posts";
export async function get() {
return {
body: await getFirebaseDoc()
};
}
routes/content.svelte
export async function load({ fetch }) {
const res = await fetch('/resources');
if (res.ok) {
return {
props: { resources: await res.json() }
};
}
return {
status: res.status,
error: new Error()
};
}
This seems extraneous and problematic as I speak of here, but it also seems like the only way.
J
You need to use a handler that injects the cookie into the server response (because load functions do not expose the request or headers to the browser, they are just used for loading props I believe). Example here: https://github.com/sveltejs/kit/blob/59358960ff2c32d714c47957a2350f459b9ccba8/packages/kit/test/apps/basics/src/hooks.js#L42
https://kit.svelte.dev/docs/hooks#handle
export async function handle({ event, resolve }) {
event.locals.user = await getUserInformation(event.request.headers.get('cookie'));
const response = await resolve(event);
response.headers.set('x-custom-header', 'potato');
response.headers.append('set-cookie', 'name=SvelteKit; path=/; HttpOnly');
return response;
}
FYI: This functionality was only added 11 days ago in #sveltejs/kit#1.0.0-next.267: https://github.com/sveltejs/kit/pull/3631
No need to use fetch!
You can get the data however you like!
<script context="module">
import db from '$/firebaseConfig'
export async function load() {
const eventref = db.ref('cats/whiskers');
const snapshot = await eventref.once('value');
const res = snapshot.val();
return { props: { myData: res.data } } // return data under `props` key will be passed to component
}
</script>
<script>
export let myData //data gets injected into your component
</script>
<pre>{JSON.stringify(myData, null, 4)}</pre>
Here's a quick demo on how to fetch data using axios, same principle applies for firebase: https://stackblitz.com/edit/sveltejs-kit-template-default-bpr1uq?file=src/routes/index.svelte
If you want to only load data on the server you should use an "endpoint" (https://kit.svelte.dev/docs/routing#endpoints)
My solution might solve it especially for those who work with (e.g: laravel_session), actually in your case if you want to retain the cookie data when loading on each endpoint.
What you should gonna do is to create an interface to pass the event on every api() call
interface ApiParams {
method: string;
event: RequestEvent<Record<string, string>>;
resource?: string;
data?: Record<string, unknown>;
}
Now we need to modify the default sveltekit api(), provide the whole event.
// localhost:3000/users
export const get: RequestHandler = async (event) => {
const response = await api({method: 'get', resource: 'users', event});
// ...
});
Inside your api() function, set your event.locals but make sure to update your app.d.ts
// app.d.ts
declare namespace App {
interface Locals {
r: string;
}
//...
}
// api.ts
export async function api(params: ApiParams) {
// ...
params.event.locals.r = response.headers.get('r')
});
Lastly, update your hooks.ts
/** #type {import('#sveltejs/kit').Handle} */
export const handle: Handle = async ({ event, resolve }) => {
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
const response = await resolve(event);
if (!cookies.whatevercookie && event.locals.r) {
response.headers.set(
'set-cookie',
cookie.serialize('whatevercookie', event.locals.r, {
path: '/',
httpOnly: true
})
);
}
return response;
});
Refer to my project:
hooks.ts
app.d.ts
_api.ts
index.ts
I have an ember service thats primary concern is to fetch data for a specific model and the descendants of the model. The reason I am using this in a service is because the route for this particular type is using a slug which is not the primary key and therefore needs to do a store.query instead of store.find. When we fetch this model I have some logic that peeks the ember store to see if we can load it from there before going to the api query. Also this vendor is watching for the slug change and updating the current model based on that.
The problem I am having is that this seems to have very little documentation when it comes to how to test a thing like this. In fact I don't see a section on testing services anywhere in the guides here http://guides.emberjs.com/v2.1.0/
This is a snippet of the service in question.
import Ember from 'ember';
export default Ember.Service.extend({
_vendorSlug: null,
vendor: null,
vendorSlug: function (key, value) {
if (arguments.length > 1) {
if (this._vendorSlug) {
return this._vendorSlug;
}
this._vendorSlug = value;
}
return this._vendorSlug;
}.property(),
ensureVendorLoaded: function (slug) {
var service = this,
vendorSlug = slug || service.get('vendorSlug'),
currentVendor = service.get('vendor'),
storedVendor;
if (!Ember.isNone(currentVendor) && (vendorSlug === currentVendor.get('slug'))) {
return new Ember.RSVP.Promise((resolve) => {
resolve(currentVendor);
});
} else {
var storedVendors = service.store.peekAll('vendor').filter((vendor) => {
return vendor.get('slug') === vendorSlug;
});
if (storedVendors.length) {
storedVendor = storedVendors[0];
}
}
if (!Ember.isNone(storedVendor)) {
service.set('vendorSlug', storedVendor.get('slug'));
return new Ember.RSVP.Promise((resolve) => {
resolve(storedVendor);
});
}
return service.store.queryRecord('vendor', {slug: vendorSlug}).then((vendor) => {
service.set('vendor', vendor);
service.set('vendorSlug', vendor.get('slug'));
return vendor;
});
},
_vendorSlugChanged: function () {
if (this.get("vendorSlug") === this.get("vendor.slug")) {
return;
}
this.ensureVendorLoaded();
}.observes('vendorSlug')
});
I would like to be able to assert a couple of scenarios here with the store interaction. Vendor already set, vendor loaded from the peek filter, and vendor loaded from query.
I think I have finally come to a reasonable conclusion. Let me share with you what I think may be the best way to approach unit testing services that rely on the store.
The answer really lies in the assumption we must make when writing unit tests. That is, everything outside of our logical unit should be considered to work properly and our units should be completely independent.
Thus, with a service relying on the store it is best to create a stub or mock (see this question to understand the difference between a mock and a stub) for the store. A stub for the store itself is quite simple. Something like this will do:
store: {
find: function() {
var mockedModel = Ember.Object.create({/*empty*/});
return mockedModel;
},
query: ...
}
If you prefer to use a mock instead you could do something like the following (i made this really fast so it might not work completely but its enough to get the idea across):
import Ember from 'ember';
class MockStore {
constructor() {
this.models = Ember.A([]);
}
createRecord(modelName, record) {
// add a save method to the record
record.save = () => {
return new Ember.RSVP.Promise((resolve) => {
resolve(true);
});
};
if (!this.models[modelName]) {
this.models[modelName] = Ember.A([]);
}
this.models[modelName].pushObject(record);
return record;
}
query(modelName, query) {
let self = this;
return new Ember.RSVP.Promise((resolve) => {
let model = self.models[modelName];
// find the models that match the query
let results = model.filter((item) => {
let result = true;
for (let prop in query) {
if (query.hasOwnProperty(prop)) {
if (!item[prop]) {
result = false;
}
else if (query[prop] !== item[prop]) {
result = false;
}
}
}
return result;
});
resolve(results);
});
}
}
export default MockStore;
Next all you have to do is to set the store property (or whatever your calling it) on your service to a new mock store instance when you run a test. I did this like so:
import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';
import MockStore from '../../helpers/mock-store';
let session;
let store;
const username = '';
const password = '';
moduleFor('service:authentication', 'Unit | Service | authentication', {
beforeEach() {
session = Ember.Object.create({});
store = new MockStore();
}
});
test('it should authenticate the user', function (assert) {
// this sets the store property of the service to the mock store
let authService = this.subject({session: session, store: store});
authService.authenticate(username, password).then(() => {
assert.ok(session.get('username'));
});
});
The documentation on testing these situations is definitely poor, so perhaps there is a better method, but this is what I will be rolling with for now. Also, if you check out the Discourse project, which uses ember, they follow a similar pattern to what I described here, but in a little more advanced manner.
I'm not sure this is the answer you want, but I'll give it a shot anyway. An Ember Service is not really much more than an Ember Object and if you're "unit testing" that Service, it should be in isolation of its dependencies (otherwise it wouldn't be a unit test).
From my understanding (and this could be wrong). If you want to test that service you need to replace the store with a mock implementation.
//tests/unit/services/my-service.js
test('some scenario', function(assert) {
let service = this.subject({
store: Ember.Object.create({
peekAll(modelName){
//Return array for this scenario
},
query(model, params){
//Return array for this scenario
}
});
});
assert.ok(service);
});
I also think this is why there's little documentation testing services.
One resource I recommend about services is this talk from the Chicago Ember Meetup
I'm loading a route. Its model hook loads some models. Some are fetch from ember store and some are promises requested through AJAX:
model: function () {
return Em.RSVP.hash({
//the server data might not be loaded if user is offline (application runs using appcache, but it's nice to have)
someServerData: App.DataService.get(),
users: this.store.find('user')
});
}
The App.DataService.get() is defined as:
get: function () {
return new Ember.RSVP.Promise(function(resolve, reject) {
//ajax request here
});
}
Obviously if the request is rejected, the flow is interrupted and I cannot display the page at all.
Is there a way to overcome this?
Ember.RSVP.hashSettled is exactly meant for this purpose.
From tildeio/rsvp.js Github repository:
hashSettled() work exactly like hash(), except that it fulfill with a hash of the constituent promises' result states. Each state object will either indicate fulfillment or rejection, and provide the corresponding value or reason. The states will take one of the following formats:
{ state: 'fulfilled', value: value }
or
{ state: 'rejected', reason: reason }
Here is an example for using it (working JS Bin example):
App.IndexRoute = Ember.Route.extend({
fallbackValues: {
firstProperty: null,
secondProperty: null
},
model: function() {
var fallbackValues = this.get('fallbackValues');
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.RSVP.hashSettled({
firstProperty: Ember.RSVP.Promise.resolve('Resolved data despite error'),
secondProperty: (function() {
var doomedToBeRejected = $.Deferred();
doomedToBeRejected.reject({
error: 'some error message'
});
return doomedToBeRejected.promise();
})()
}).then(function(result) {
var objectToResolve = {};
Ember.keys(result).forEach(function(key) {
objectToResolve[key] = result[key].state === 'fulfilled' ? result[key].value : fallbackValues[key];
});
resolve(objectToResolve);
}).catch(function(error) {
reject(error);
});
});
}
});
fallbackValues can be useful for managing resolved hash's properties' fallback values without using conditions inside the promise function.
Taking into account that Ember.RSVP.hashSettled is not available in my Ember version. I come up with the following solution:
model: function(params) {
var self = this;
return new Em.RSVP.Promise(function(resolve, reject){
// get data from server
App.DataService.get().then(function(serverData) { //if server responds set it to the promise
resolve({
serverData: serverData,
users: self.store.find('user')
});
}, function(reason){ //if not ignore it, and send the rest of the data
resolve({
users: self.store.find('user')
});
});
});
}
I'm trying to create a User.current() in my application, which pulls data from my server using $.getJSON('/users/current', function(data) { ... });. I am using the Singleton method that Discourse uses, which does the following:
Dashboard.Singleton = Ember.Mixin.create({
// See https://github.com/discourse/discourse/blob/master/app/assets/javascripts/discourse/mixins/singleton.js
current: function() {
if (!this._current) {
this._current = this.createCurrent();
}
return this._current;
},
createCurrent: function() {
return this.create({});
}
});
And in my User singleton model, I've rewritten createCurrent as follows:
Dashboard.User.reopenClass(Dashboard.Singleton, {
createCurrent: function() {
return Ember.Deferred.promise(function(p) {
return p.resolve($.getJSON('/users/current').then(function(data) {
return Dashboard.User.create(data);
}));
});
}
});
User is a normal Ember object model:
Dashboard.User = Ember.Object.extend({
});
This does request the data from the server, but the function is not setting User.current() correctly - when I inspect it, User.current() has none of the properties that should be set, such as name.
How can I return and set the current user using Ember's deferred and promises?
That's cause you're returning the promise in place of the user.
Why don't you create the user, then fill in the properties later.
Or use the Promise Proxy pattern that Ember Data uses (the promise can be used as the object once resolved)
DS.PromiseObject = Ember.ObjectProxy.extend(Ember.PromiseProxyMixin);
function promiseObject(promise) {
return DS.PromiseObject.create({ promise: promise });
}
Since $.getJSON('/users/current') returns a promise, might as well use that.
createCurrent: function() {
return $.getJSON('/users/current').then(function(data) {
return Dashboard.User.create(data);
});
}
Then you need to keep in mind that createCurrent returns a promise, not the object itself so you will need to:
current: function() {
if (!this._current) {
var that = this;
this.fetching = true;
this.createCurrent().then(function(val) {
that.fetching = false;
that._current = val;
});
}
return this._current;
},