Ember js: retrieving all documents from couchdb - ember.js

Well I am new to ember js and i am stuck at a place. I wanted to know how can one use ember-cli-sofa to retrieve all the documents at once? Well I am using couchdb as backend and ember-cli-sofa helps to interact with database.Please give any tips how to approach this problem.

If you're fine without using models,
let db = store.get('db.main.documents');
db.all({ include_docs: true }); // => Promise
db.view('ddocName', 'viewName', { include_docs: true }); // => Promise
db.all calls _all_docs while db.view calls /_design/<ddoc>/_view/<view>

Related

Resolving keypaths (or chained relationships) in route

I am processing a bunch of records and producing an array of hashes for a third party library. For the life of me I can't figure out why this doesn't work.
export default Route.extend({
model: function(params) {
let qp = {viewName: 'byDay'};
return this.store.query('job-receipt', qp).then(
(receipts)=>
all(
receipts.map(
(receipt)=>
hash({
stockCode: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('stockCode')),
productClass: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('productClass'))
.then(productClass => productClass.get('descr')),
qtyRecvd: receipt.get('qtyRecvd')
})
)
)
);
If I keep reentering the route, eventually the promises resolve. If I check, the productClass promise just straight up gets called with a null value. Why isn't it waiting for the stockCode.get('productClass') to resolve? I know there are real values in there because it does eventually resolve.
I'm missing something super basic. I've tried Ember.get(thing, keypath) etc. Don't these all return promises? Isn't the RSVP.hash supposed to wait for all the promises to resolve before proceeding? Like I said, I know the data is good because eventually it does resolve (as opposed to me just not handling a rejection).
EDIT:
I changed the productClass promise to this:
productClass: receipt.get('job')
.then(job => job.get('stockCode'))
.then(stockCode => stockCode.get('productClass'))
.then(productClass => {if (!productClass) {return 'foo';} return productClass.get('descr');})
Now the report renders correctly every time albeit with nonsense. If I navigate to another route, and then back to this route, it renders perfectly. So, that makes it hard for me to believe I have some kind of data error. And even some of the stock codes return the right product class - not 'foo' - on the first run through. I'm not even sure how to debug this further.
edit
Just saw this. May be a bug after all.
[3.2.0+] Snapshot’s related data has become null #5565
It turns out it is a bug. The bug for belongs-to.js causes the model to not wait for internalModel to complete loading before resolving the promise. The fix linked below resolves the issue
[BUGFIX] use internalModel promise if already loading #5562
I think the main issue is that "reciepts.job" is likely a DS.belongsTo relationship right? If you switch it to load job: DS.belongsTo('job', {async: false}), that will force ember-data to load that property synchronously (and will save a lot of headaches). But that requires the data to be available either in the json response.
If that doesn't work, you should investigate ember concurrency. Use that to clean up your code to look a little more straightforward. You'll have to fill in some blanks or change things where I misunderstood your use case, but this is a likely a good starting point.
The idea is to continue to break all the asynchronous calls into seperate tasks. Each ember-concurrency task object returns a promise, so you can keep bundling them up until you get to model which you can return just like any other promise.
//model.js
import {task} from 'ember-concurrency';
.....
model() {
return this.get('loadData').perform();
},
loadData: task(function*(){
let reciepts = yield this.store.query('job-receipt', qp);
let promises = reciepts.map(r => {
return this.get('loadNestedData').perform(r);
})
return all(promises)
}),
loadNestedData: task(function*(reciept) {
let job = yield receipt.get('job');
return hash({
stockCode: job.get('sockcode')
});
})

Preserve cookies / localStorage session across tests in Cypress

I want to save/persist/preserve a cookie or localStorage token that is set by a cy.request(), so that I don't have to use a custom command to login on every test. This should work for tokens like jwt (json web tokens) that are stored in the client's localStorage.
To update this thread, there is already a better solution available for preserving cookies (by #bkucera); but now there is a workaround available now to save and restore local storage between the tests (in case needed). I recently faced this issue; and found this solution working.
This solution is by using helper commands and consuming them inside the tests,
Inside - cypress/support/<some_command>.js
let LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add("saveLocalStorage", () => {
Object.keys(localStorage).forEach(key => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add("restoreLocalStorage", () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});
Then in test,
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
Reference: https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888
From the Cypress docs
For persisting cookies: By default, Cypress automatically clears all cookies before each test to prevent state from building up.
You can configure specific cookies to be preserved across tests using the Cypress.Cookies api:
// now any cookie with the name 'session_id' will
// not be cleared before each test runs
Cypress.Cookies.defaults({
preserve: "session_id"
})
NOTE: Before Cypress v5.0 the configuration key is "whitelist", not "preserve".
For persisting localStorage: It's not built in ATM, but you can achieve it manually right now because the method thats clear local storage is publicly exposed as Cypress.LocalStorage.clear.
You can backup this method and override it based on the keys sent in.
const clear = Cypress.LocalStorage.clear
Cypress.LocalStorage.clear = function (keys, ls, rs) {
// do something with the keys here
if (keys) {
return clear.apply(this, arguments)
}
}
You can add your own login command to Cypress, and use the cypress-localstorage-commands package to persist localStorage between tests.
In support/commands:
import "cypress-localstorage-commands";
Cypress.Commands.add('loginAs', (UserEmail, UserPwd) => {
cy.request({
method: 'POST',
url: "/loginWithToken",
body: {
user: {
email: UserEmail,
password: UserPwd,
}
}
})
.its('body')
.then((body) => {
cy.setLocalStorage("accessToken", body.accessToken);
cy.setLocalStorage("refreshToken", body.refreshToken);
});
});
Inside your tests:
describe("when user FOO is logged in", ()=> {
before(() => {
cy.loginAs("foo#foo.com", "fooPassword");
cy.saveLocalStorage();
});
beforeEach(() => {
cy.visit("/your-private-page");
cy.restoreLocalStorage();
});
it('should exist accessToken in localStorage', () => {
cy.getLocalStorage("accessToken").should("exist");
});
it('should exist refreshToken in localStorage', () => {
cy.getLocalStorage("refreshToken").should("exist");
});
});
Here is the solution that worked for me:
Cypress.LocalStorage.clear = function (keys, ls, rs) {
return;
before(() => {
LocalStorage.clear();
Login();
})
Control of cookie clearing is supported by Cypress: https://docs.cypress.io/api/cypress-api/cookies.html
I'm not sure about local storage, but for cookies, I ended up doing the following to store all cookies between tests once.
beforeEach(function () {
cy.getCookies().then(cookies => {
const namesOfCookies = cookies.map(c => c.name)
Cypress.Cookies.preserveOnce(...namesOfCookies)
})
})
According to the documentation, Cypress.Cookies.defaults will maintain the changes for every test run after that. In my opinion, this is not ideal as this increases test suite coupling.
I added a more robust response in this Cypress issue: https://github.com/cypress-io/cypress/issues/959#issuecomment-828077512
I know this is an old question but wanted to share my solution either way in case someone needs it.
For keeping a google token cookie, there is a library called
cypress-social-login. It seems to have other OAuth providers as a milestone.
It's recommended by the cypress team and can be found on the cypress plugin page.
https://github.com/lirantal/cypress-social-logins
This Cypress library makes it possible to perform third-party logins
(think oauth) for services such as GitHub, Google or Facebook.
It does so by delegating the login process to a puppeteer flow that
performs the login and returns the cookies for the application under
test so they can be set by the calling Cypress flow for the duration
of the test.
I can see suggestions to use whitelist. But it does not seem to work during cypress run.
Tried below methods in before() and beforeEach() respectively:
Cypress.Cookies.defaults({
whitelist: "token"
})
and
Cypress.Cookies.preserveOnce('token');
But none seemed to work. But either method working fine while cypress open i.e. GUI mode. Any ideas where I am coming short?
2023 Updated on Cypress v12 or more:
Since Cypress Version 12 you can use the new cy.session()
it cache and restore cookies, localStorage, and sessionStorage (i.e. session data) in order to recreate a consistent browser context between tests.
Here's how to use it
// Caching session when logging in via page visit
cy.session(name, () => {
cy.visit('/login')
cy.get('[data-test=name]').type(name)
cy.get('[data-test=password]').type('s3cr3t')
cy.get('form').contains('Log In').click()
cy.url().should('contain', '/login-successful')
})

Continuous data update via pouchdb query

i have a pouchdb database with a number of views setup that i query whenever i need data. I am using observables to handle the querying. However i have to refresh the interface to view any data changes in the database. Is there any way i can have these data changes read directly by the observable ? My code is as:-
home.ts
this.postsService.getPosts().subscribe((posts) => {
this.posts = posts.rows.map(row => {
console.log(row.value);
return row.value;
});
});
posts.ts
getPosts(): Observable<any> {
return Observable.fromPromise(this.db.query('app/inputs'));
}
You can use use db.changes with your view, so that you'll only get events for view related changes:
db.changes({
filter: '_view',
view: 'app/inputs',
live: true,
since: 'now',
include_docs:true
}).on('change', (change) => { this.handleChange(change); });
See the filtered changes section on PouchDB docs for a more detailed explanation on this.

Get two versions of ember-simple-auth to play well together

We're working with two ember applications that each run different version of ember and ember-simple-auth, and want to get ember-simple-auth to work well with both version.
The old app
Ember 1.8.1
Ember-simple-auth 0.7.3
The new app
Ember 2.3.1
Ember-simple-auth 1.0.1
Uses cookie session store
We trying to change the session API for the older version so that it stores the access and refresh tokens correctly so the new app can use it.
So far, we’ve tried overriding the setup and updateStore methods to work with the authenticated nested object but are still running into issues.
Disclaimer - Patrick Berkeley and I work together. We found a solution after posting this question that I figured I would share.
In order for a 0.7.3 version of ember-simple-auth's cookie store to play nicely with a 1.0.0 version, we did have to normalize how the cookie was being formatted on the app with the earlier version in a few key places, mostly centered around the session object (the 0.7.3 session is an ObjectProxy that can be extended in the consuming app to create your own custom session).
The methods that we needed to override, centered around the structure of data being passed to the cookie store to persist and what was being returned when a session was being restored. The key difference is on version 0.7.3, the access_token, etc is stored top-level on the content object property of the session. With 1.0.0. this is nested inside another object inside content with the property name of authenticated. We therefore needed to ensure that everywhere we were making the assumption to set or get the access_token at the top level, we should instead retrieve one level deeper. With that in mind, we came up with these methods being overridden in our custom session object:
// alias access_token to point to new place
access_token: Ember.computed.alias('content.authenticated.access_token'),
// overridden methods to handle v2 cookie structure
restore: function() {
return new Ember.RSVP.Promise((resolve, reject) => {
const restoredContent = this.store.restore();
const authenticator = restoredContent.authenticated.authenticator;
if (!!authenticator) {
delete restoredContent.authenticated.authenticator;
this.container.lookup(authenticator).restore(restoredContent.authenticated).then(function(content) {
this.setup(authenticator, content);
resolve();
}, () => {
this.store.clear();
reject();
});
} else {
this.store.clear();
reject();
}
});
},
updateStore: function() {
let data = this.content;
if (!Ember.isEmpty(this.authenticator)) {
Ember.set(data, 'authenticated', Ember.merge({ authenticator: this.authenticator }, data.authenticated || {}));
}
if (!Ember.isEmpty(data)) {
this.store.persist(data);
}
},
setup(authenticator, authenticatedContent, trigger) {
trigger = !!trigger && !this.get('isAuthenticated');
this.beginPropertyChanges();
this.setProperties({
isAuthenticated: true,
authenticator
});
Ember.set(this, 'content.authenticated', authenticatedContent);
this.bindToAuthenticatorEvents();
this.updateStore();
this.endPropertyChanges();
if (trigger) {
this.trigger('sessionAuthenticationSucceeded');
}
},
clear: function(trigger) {
trigger = !!trigger && this.get('isAuthenticated');
this.beginPropertyChanges();
this.setProperties({
isAuthenticated: false,
authenticator: null
});
Ember.set(this.content, 'authenticated', {});
this.store.clear();
this.endPropertyChanges();
if (trigger) {
this.trigger('sessionInvalidationSucceeded');
}
},
bindToStoreEvents: function() {
this.store.on('sessionDataUpdated', (content) => {
const authenticator = content.authenticated.authenticator;
this.set('content', content);
if (!!authenticator) {
delete content.authenticated.authenticator;
this.container.lookup(authenticator).restore(content.authenticated).then((content) => {
this.setup(authenticator, content, true);
}, () => {
this.clear(true);
});
} else {
this.clear(true);
}
});
}.observes('store'),
This took us most of the way there. We just needed to ensure that the authenticator name that we use matches the name on 1.0.0. Instead of 'simple-auth-authenticator:oauth2-password-grant', we needed to rename our authenticator via an initializer to 'authenticator:oauth2'. This ensures that the apps with the newer version will be able to handle the correct authenticator events when the cookie session data changes. The initializer logic is simple enough:
import OAuth2 from 'simple-auth-oauth2/authenticators/oauth2';
export default {
name: 'oauth2',
before: 'simple-auth',
initialize: function(container) {
container.register('authenticator:oauth2', OAuth2);
}
};
The above satisfies our needs- we can sign in to an app using ember-simple-auth 0.7.3 and have the cookie session stored and formatted properly to be handled by another app on ember-simple-auth 1.0.0.
Ideally, we would just update the Ember and Ember Simple Auth versions of the app though business needs and the fact that we wanted to focus our energies on the v2 versions (which are completely fresh and new code bases) propelled us to go down this path.

Ember.js, throttle REST adapter calls

I am using Parse, which only allows 30 reqs/sec to their backend.
Therefore, I'd like to throttle all calls going to Parse via the REST adapter (specifically the ember-parse-adapter, which extends the DS.RESTAdapter).
I tried throttling the ajax method, which I assumed needed to return a Promise:
export default ParseAdapter.extend({
applicationId: ENV.APP.applicationId,
restApiId: ENV.APP.restApiId,
ajax: function(url, type, options) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.run.later(this,resolve,5000); // I would prefer this to be Ember.run.throttle, but not sure if that will work
}).then(function() {
return self._super(url,type,options)
});
}
});
However I get this error:
TypeError: Cannot read property 'results' of undefined
at exports.default.DS.default.RESTSerializer.extend.extractArray (vendor.js:115817)
at apply (vendor.js:30197)
at superWrapper (vendor.js:29749)
at ember$data$lib$system$serializer$$default.extend.extractFindAll (vendor.js:81161)
at ember$data$lib$system$serializer$$default.extend.extract (vendor.js:81144)
at superFunction [as _super] (vendor.js:25863)
at exports.default.DS.default.RESTSerializer.extend.extract (vendor.js:115843)
at apply (vendor.js:30197)
at superWrapper [as extract] (vendor.js:29749)
What can I do to throttle the REST Adapter?
If I understand you correctly, you want to batch API requests together?
I don't know the best way to do this myself, however a good place to start looking might be this Ember CLI plugin, 'ember-model-batch'
Direct link to source code