I would like to create a custom api service that will work similarly to the supabase-js library. Example:
const { data, error } = await supabase
.from('cities')
.select('name, countries(*)')
.eq('countries.name', 'Estonia')
I cannot figure out how it is possible that the last method doesn't have to be something like .get() (which would perform the fetch request)
Ideally, I would like the fetch to be called automatically after the chained methods are resolved - sth. like this:
const { data, pending, error } = await useProductsService()
.whereIn('id', [123, 12, 521, 521])
.whereLowerThan('price', 300)
note: I used supabase just as an example, I want to use this with a completely different API
I know how to do it with sth like a .get() method at the end, however, I'd like to do it without it, if possible.
It's not a big deal to have to call one more method but I see it as an opportunity to learn something new as well.
Is it possible to do something like this? Or did I understand something wrong?
As you correctly point out, it is necessary to have a function call at the very end, to let the chain know that you have finished building it and that it's time to execute the DB query.
However, when you do the destructuring and extract the data property, if there is a getter function for data, then that will act as the function which is called that signals that the building of the query is complete.
class Chain {
from(s) {
this.config = []; // 'from' is always the first in the chain, so reset the config
this.config.push({from: s}); return this;
}
select(s) { this.config.push({select: s}); return this; }
eq(s) { this.config.push({eq: s}); return this; }
get data() {
console.log(this.config);
// do the query here
return 'this is the result';
}
}
const api = new Chain()
const { data } = api
.from('cities')
.select('name, countries(*)')
.eq('countries.name', 'Estonia')
console.log(data)
Related
I am creating an observable like so:
return new Observable(sub => {
const {next, complete, error} = sub;
this.AuthHttp.get(`http://192.168.1.100:3000/api/users/${id}`)
.subscribe(res => {
let user = res.json();
next(user);
complete();
}, e => {
error(e.json());
});
})
Yet it nothing is happening in my front end when next() is expected to be called. If I make a minor change to the code so that sub.next() is called instead, everything works as expected. This indicates the underlying code is not flawed, just the way I am making a reference to next.
I have seen this form of destructuring used with the Observer class before (in an example online), so what am I doing wrong here?
Because the next, error and complete methods are object methods that must be called on an object instance.
When you use destructuring to obtain the functions and later call those functions, the calls are without context.
You cannot do what you've attempted for the same reason that this will not work:
const { toString } = new Date();
console.log(toString());
For more information, see this issue.
There is a certain syntax that is baffling me and i see it with the map function and also with the observable in typescript/Angular (Angular 5). I have two methods:
This one is in a component:
logout() {
this.authService.logout().subscribe(
result => {
this.router.navigate(['/login']);
}
);
}
And this is in the related service:
logout(): Observable<any> {
return this.http.post('/api/auth/logout', { }).map(
response => {
this._token = null;
//more unrelated code...
return true
}
);
}
The part that is confusing me in both of these cases is this:
thing => {
//code
}
What is this? The code above works. but I see that have both 'result' and 'response' for thing. Can 'thing' be anything at all or is it defined somewhere?
Also, I looked up the map function in javascript at w3schools (because I've never had a use for it) and it shows in the example that the first parameter is supposed to be a function which gets applied to each element of the array that it is associated with but "thing => {}" is not a function so this is super confusing.
Note, that I have worded my question in such a way as to get to the underlying misunderstanding rather than focusing on my specific problem, however solving my specific problem may help illustrate my misunderstanding.
The problem with the code above is that while it works it does not know what to do when the api endpoint returns a 500 error. I am trying to determine how to catch the error so that I can do something with that on the front end.
Thing can be whatever you want to name it. Result, data, response, etc. Doesn't matter. What you're basically doing is creating a variable for the result emitted from your subscription. The subscription takes in a function() and inside that function, you pass the variable name that you want to be used for the success result. And really, here, using result is meaningless, since nothing is ever done with it. If you aren't going to do anything with the response, its better to just say:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
});
}
To catch errors, you only need to pass a comma after the last curly, like so:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
}, err => {
// Do something with error here
});
}
As for map, here is an example
var array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x * 2);
It basically takes every variable in the array and performs that map method, meaning it takes each value and does whatever the function says to do, in this case, multiply it by 2. Think of it as a sort of transformation. In that example, it's basically being used to manipulate the response before sending it back to the subscription.
Let me start by saying this is more of a curiosity question because, as you will see, I was able to achieve the desired functionality.
However, given that javascript is a super flexible language, I would like to see what other devs might think about this problem:
I have an instance of a class which is returned from a vendor function:
const connection = vendorDatabaseLib.createConnection();
Now, I would like to create a decorator which will add functionality to the connection class, for example, reconnection logic.
Lets call it PersistentConnection. Apart from my added custom functions I would like an instance of PersistentConnection to forward all function calls to the original Connection instance. And in some functions override the behaviour.
I could of course implement all Connection's functions explicitly and forward them to the inner object but there might be lots of these functions, so I quickly discarded this idea.
So here are my ideas of how to achieve this:
Monkey patching 🐒, Instead of a decorator I can create a PersistentConnection class which inherits from the vendor Connection and then patch the vendor vendorDatabaseLib.createConnection function to return PersistentConnection with all my desired added functionality. Tempting, but bad.
Create a decorator which iterates over the Connection functions and creates forwards dynamically, something like:
class PersistentConnection{
constructor(connection){
this._connection = connection;
// Iterate through all functions
for (prop in this._connection){
if(typeof(this._connection[prop]) === 'function'){
// Create functions dynamically for each forward
this[prop] = (...args) => {
this._connection[prop](...args);
}
}
}
}
// This is the added logic
reconnect(){
// Custom logic
}
}
Set the Connection instance to be a the prototype of PersistentConnection's instance:
function persistenChannel(channel){
const persistentChannel = {};
Object.setPrototypeOf(persistentChannel, channel);
persistentChannel.reconnect = () => {
// custom logic
}
}
This is the most "automatic" way I could think of.. But it just down right ugly, and the custom functions need to be declared each time an instance is created.
I still feel like I'm missing something, something like Ruby's magical method_missing (or pythons __getattr__) function which is called just before a method is missing exception is thrown and lets you define "safety net" logic (like delegating all calls to the inner _connection object.
Is there a better way to achieve this functionality?
Thanks a lot [=
Lets start from what we have. In any case, most of the functionaliy will be performed by vendor object. We do not know details realization so we can't rely that this object has no state. This mean, that in any case we need new connection object for the new persistentConnection. This can be achieved with proxy object
Lets try to do this:
function Connection() {
this.connect = () => console.log('connected by Connection class');
this.disconnect = () => console.log('disconnected by Connection class');
}
function persistantConnectionFactory() {
function PersistentConnection() {
this.checkConnection = () => console.log('no connection');
}
const instance = new PersistentConnection();
const proxy = new Proxy(instance, {
get: function (target, name) {
if (!(name in target)) {
console.log('adding new prototype')
Object.setPrototypeOf(instance, new Connection())
}
return target[name];
}
});
return proxy;
}
var c = persistantConnectionFactory();
c.checkConnection();
c.connect();
Does this solution good? I think - not. Without very good reasons this adds complexity without any value. Prototype should be enough.
I have an helper function that returns an array instead of the conventional db.dbName.find() cursor. How do I code a return array so it reflects as a cursor similar to one generated by a db.dbName.find() that I can use in template?
Find below my helper function:
var arrayTest = Meteor.call('getUserCategoryArray', function(error, results){
if(error){
console.log(error.reason);
} else {
var results1 = results.toString();
var results2 = results1.split(",");
alert("Array content: " +results2);
alert(results2[0]);
alert(results2[1]);
alert(results2[2]);
return results2;
}
})
To explain part of the code: From the top down: The alerts successfully prints out:
Array content: shoes,clothes,watches
shoes
clothes
watches
The alert is just to confirm that results2 is a working array.
Now how do I code the return value/array so that I am able to use it in my template as if it was a cursor returned by a db.dbName.find() query?
Your help is appreciated.
your issue isn't about arrays, it's about synchronous vs asynchronous programming. as #mutdmour mentioned, spacebars can handle an array from a helper just fine.
helpers can get called several times as a template is rendered, so it shouldn't do anything async or have any side effects. your helper is making an async call, so that's one issue right off the bat.
the issue you're seeing is that such a call is async, and a helper needs to be sync. so you'll have trouble getting your helper to work as-is.
in many cases, helpers return the contents of, or a cursor to the contents of, a collection. i don't know your app, but is a publish/subscribe with collection contents a better choice here?
if not, and it has to be the results from a method call, then generally i will:
make the call in the onCreated()
write the results to a reactive var
return the reactive var from the helper
e.g.
Template.Foo.onCreated(function() {
let self = this;
self.clothing = new ReactiveVar();
Meteor.call('getUserCategoryArray', function(error, results) {
if (!error) {
// or whatever you need to do to get the results into an array
self.clothing.set(results);
}
});
});
Template.Foo.helpers({
clothing() {
return Template.instance().clothing.get();
}
});
I'm creating functional tests using theintern framework. I want to model my tests using "page objects" since I would like the code to be reusable.
In the original documentation, there is a very simplified sample that shows how to create a page object with one method called 'login'.
In this example, all the logic of this method is inside the method itself.
I would like to create a page object that represents a page a little more complex than a login page, and be able to reuse components inside the page for different actions.
Here's an example of what I want to do :
// in tests/support/pages/IndexPage.js
define(function (require) {
// the page object is created as a constructor
// so we can provide the remote Command object
// at runtime
function IndexPage(remote) {
this.remote = remote;
}
function enterUsername(username) {
return this.remote
.findById('login').click().type(username).end();
}
function enterPassword(pass) {
return this.remote
.findById('password').click().type(pass).end();
}
IndexPage.prototype = {
constructor: IndexPage,
// the login function accepts username and password
// and returns a promise that resolves to `true` on
// success or rejects with an error on failure
login: function (username, password) {
return this
.enterUsername(username)
.enterPassword(password)
.findById('loginButton')
.click()
.end()
// then, we verify the success of the action by
// looking for a login success marker on the page
.setFindTimeout(5000)
.findById('loginSuccess')
.then(function () {
// if it succeeds, resolve to `true`; otherwise
// allow the error from whichever previous
// operation failed to reject the final promise
return true;
});
},
// …additional page interaction tasks…
};
return IndexPage;
});
Notice how I created the enterUsername and enterPassword methods.
This is because I would like to reuse these methods in other tests of the same page object. The problem with this is that I can't chain these methods, it doesn't work.
The methods that can be chained all return the Command object, but when I chain my methods, they aren't defined on the Command method so the first method gets called (in my example this is enterUsername), but then the second fails, obviously since enterPassword isn't defined on the Command object.
I would like to know how I can model my Page Objects so I can reuse parts of the code within the page object, but still have a nice fluent syntax like this.
Thanks in advance :)
The simplest solution is to use your methods as then callback handlers, like:
function enterName(username) {
return function () {
return this.parent.findById('login').click().type(username);
}
}
function enterPassword(password) {
return function () {
return this.parent.findById('password').click().type(pass).end();
}
}
IndexPage.prototype = {
constructor: IndexPage,
login: function (username, password) {
return this.remote
.then(enterUsername(username))
.then(enterPassword(password))
.findById('loginButton')
// ...
}
}