Combined resolvers always throw "cannot read property 'apply' of undefined" error - graphql

I am trying to attach the following guard:
export const isSupplier=(_,__,{me})=>{
me.role==="supplier" ? skip : new ForbiddenError('Not authenticated as supplier.');
}
To a resolver, like this:
addProductNow:combineResolvers(
isSupplier,
async (_,{name,price,description,url,stock},{dataSources,me})=>{
const supplierId=me.id
console.log(supplierId)
const supplier=await dataSources.SupplierAPI.findSupplierById(supplierId)
const newProduct=await dataSources.ProductAPI.addProduct(name,price,description,url,stock,supplierId,{ttlInSeconds:60*20})
return newProduct
}),
Yet it always returns the error "cannot read property 'apply' of undefined". I have tried to log something in the guard , yet it seems like it never gets executed. After removing the guard from the resolver everything works fine and logging 'me' shows the expected value. Am I doing something wrong ? Thanks !

I'm not sure what you're using for your "guards", but I assume it's because you're not doing or returning anything in this function, and it's expecting a function (what it calls "apply" on):
export const isSupplier=(_,__,{me})=>{
me.role==="supplier" ? skip : new ForbiddenError('Not authenticated as supplier.');
}
Did you mean to do this:
export const isSupplier=(_,__,{me})=>{
return me.role==="supplier" ? skip : new ForbiddenError('Not authenticated as supplier.');
}
Follow-up Edit:
From googling some of your variable names, I assume you're using graphql-resolvers.
In the example they provide, they use "arrow functions with implicit return", which is when you
Don't add Braces around the body of your function
Put the whole thing in one expression
The result of the function becomes whatever the expression after the arrow results in:
const isAuthenticated = (root, args, { user }) => user ? skip : new Error('Not authenticated')
This can be converted to using "explicit returns" by adding braces and the return keyword like this:
const isAuthenticated = (root, args, { user }) => {
return user ? skip : new Error('Not authenticated')
}
Your code as documented, however, added the braces but NOT the return and therefore always returns undefined. Looking at the source code of that library, if you don't return EXACTLY skip it resolves to whatever the return value of your function was (in this case undefined).
Can you share more information about your stack trace to show WHAT is trying to call .apply on what?

Related

Proper way to call RTK Query endpoint.select() with no arguments (and skip options)

I would like to use the endpoint.select() function to create selectors from cached RTK Query data, see RTK Advanced Patterns.
The documentation clearly states that if there is no query argument, you can pass undefined to select() (see the Selecting Users Data section).
However, in my case this does not work unless i trigger the query by the initiate() function. When triggering the query from the query hook on the other hand, the selector fails to retrieve the cached data.
The not working setup here is pretty simple:
export const productsApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: API.ENDPOINTS.PRODUCTS }),
reducerPath: 'productsApi',
endpoints: (builder) => ({
listAllProducts: builder.query({
query: ()=>'/list',
}),
}),
});
export const { useListAllProductsQuery } = productsApi;
Then in a customHook I call the useListAllProducts hook:
const {
data,
} = useListAllProductsQuery({skip:shouldSkip});
And finally in the selector:
export const selectProducts =
productsApi.endpoints.listAllProducts.select(); //undefined param as docs recommend
Potential Fix: (or more like a hacky workaround):
Strangely enough, I discovered that if i pass an argument (aka cacheKey) into the select function and pass that same cacheKey into the query hook, all of a sudden the stars align and everything works (although the docs state this is not necessary). So the modified code looks like:
// in selector
export const selectProducts =
productsApi.endpoints.listAllProducts.select('products');
// in hook
const {
data,
} = useListAllProductsQuery('products');
Im wondering if anyone can shed some wisdom on why this works, or even better can
recommend the best practice for utilizing the select function on a query with no cacheKey (since the docs seem incorrect or outdated?).
I would also like to point out, when calling select() without a parameter, a typescript warning surfaces indicating a parameter is required.
I am not certain where you think that the docs do state that you do not need an argument.
You will need to call select with the same argument as you call your hook to get a selector for that cache key - so if you call useMyQuery(), you can call select() - and if you call useMyQuery(5), you can call select(5) to get a selector for the cache key 5.
Those are individual cache entries and you will need a selector for each of those cache entries.
Also, could you clarify what exactly you mean by "not working"? Using the selector will give you only the cache entry, but not make a request - you are after all just selecting from the store. So before you used the hook or dispatched initiate, you will get an uninitiated cache entry.
I think, this can solve your problem
in component.jsx file
const state = useSelector((state: RootState)=>state);
console.log(getState(state, params));
in api.js file
export const getState = (state: RootState, params) => api.endpoints.getApiState.select(params)(state);

How to test GraphQL queries with fragments using jest

Problem: I would like to test a GraphQL query that lives in a .graphql file like this:
#import '../../fragments/Widget.graphql'
query WidgetFragment($id: ID) {
readWidgetFragment(id: $id) {
...Widget
}
}
To create a GraphQL schema with mocked resolvers and data, I use makeExecutableSchema and addMockFunctionsToSchema from graphql-tools.
To run the query from inside a jest test, my understanding is that I need to use the graphql() function from graphql-js.
This function needs the query as a string, so I tried two different ways, but neither of them worked:
Parse the .graphql file as a normal text file, giving me the raw string (using the jest-raw-loader in my jest config).
This gives me: Failed: Errors in query: Unknown fragment "Widget". when I run the query.
Parse the .graphql file into a query object using jest-transform-graphql. I believe this should be the right approach, because it should resolve any imported fragments properly. However, to execute the query, I need to pass query.loc.source.body to the graphql, which results in the same error message as option 1.
You can use this:
import { print } from 'graphql/language/printer'
import query from './query.gql'
...
print(query)
Use the initial approach with parsing it as a raw text, except:
use a recursive function with a path argument (assuming you could have nested fragments)
which uses regex to extract all imports beforehand to an array (maybe use a nicer pattern :) )
append the rest of the file to a string variable
then loop through imports, resolving the #imports and passing them to itself and appending the result to the string variable
Finally return the result to the main function where you pass it to the graphql()
Yes, this is quite a pickle. Even with imports correctly working (>= v2.1.0 for jest-transform-graphql, they get added to the query.definitions object, which is completely sidestepped when calling graphql with document.loc.source.body as query argument.
On the server end, graphql (function graphqlImpl) will reconstruct the document object using parse(source) - but it'll have zero knowledge of the imported fragment definitions...
As far as I can tell, the best bet is to stamp fragments to the query source before sending it to the server. You'll need to explicitly find all lines starting with #import and replace these with actual text content of the to-be-imported graphql file.
Below is the function that I use. (Not tested for recursive fragments)
// Async wrapper around dynamic `import` function
import { importQuery } from "./queries";
const importAndReplace = async (fileToImport, sourceDocument, line) => {
const doc = await importQuery(fileToImport);
const targetDocument = (await sourceDocument).replace(line, doc.loc.source.body);
return targetDocument;
};
// Inspired by `graphql-tag/loader`
// Uses promises because of async function `importQuery` used
export default async graphqlOperation => {
const { body } = graphqlOperation.loc.source;
const lines = body.split(/\r\n|\r|\n/);
const bodyWithInlineImports = await lines.reduce(
async (accumulator, line) => {
await accumulator;
const lineSplit = line.slice(1).split(" ");
return line[0] === "#" && lineSplit[0] === "import"
? importAndReplace(lineSplit[1].replace(/"/g, ""), accumulator, line)
: Promise.resolve(accumulator);
},
Promise.resolve(body)
);
return bodyWithInlineImports;
};

RxJS: How to delay execution of the ELSE in RxJS.Observable.if?

I have an object that might or might not exist. I'm using Observable.if to determine what action to do based on its existence.
However, the else part of Observable.if seems to be running even when the object is undefined. I get this error: TypeError: Cannot read property 'genesisHash' of undefined
console.log("jjjjjjjjjjjjjjjj: ", t); // prints out jjjjjjjjjjjjjjjj: undefined
return Observable.if(
() => !t,
Observable.of(nodeActions.requestGenesisHashes()),
Observable.of(
nodeActions.treasureHunt({
genesisHash: t.genesisHash // error occurs here
})
)
);
How would I delay the call to nodeActions.treasureHunt so that it doesn't try looking at the genesisHash attribute on t?
I'm using this through redux-observable by the way. Hence using actions.
Your then/else observable creation is not wrapped in a function, so the else observable setup code is ran when passing to the Observable.of(...) function. You probably need to just use regular if/else type of logic:
const actions = !t ? nodeActions.requestGenesisHashes() :
nodeActions.treasureHunt({ genesisHash: t.genesisHash });
return Observable.of(actions);
If you really wanted to use that method, you could just create the Observable manually:
return Observable.if(
() => !t,
Observable.of(nodeActions.requestGenesisHashes()),
Observable.create(obs => {
obs.next(nodeActions.treasureHunt({ genesisHash: t.genesisHash }));
obs.complete();
});
);
That should delay the use of the t variable until something tries to subscribe to the observable, which will never happen.

How to get promise value correctly?

Consider the following code:
function toolsQueryResult(){
const query = `query{......}`
return request('http://...', query,).then(data => { return data })
}
var toolsQueryResult= toolsQueryResult();
var toolsNames = [];
toolsQueryResult.then(function(result){
result['key'].forEach(function(item){
toolsNames.push(item["name"])
})
})
console.log(toolsNames)
This returns and prints out empty list "[ ]" to me.Does any one know why?
But if I put "console.log()" between two final "})", it returns list of tools correctly.How should I work with this promise object to have list of tools correctly after second "})" at the end of code?
The reason is, your console.log statement is executed before the the promise toolsQueryResult is resolved. It would be really useful and helpful if you have debugger tools on and place breakpoints to see what i just said.
That said,having the console.log outside of the promise being resolved or rejected beats the whole purpose, meaning you are trying to output a statement before it could complete its execution, hence when you place the console.log statement inside of the then function it outputs result.
Fiddle with your code (modified the result to be a simple array) for you to debug and see : https://jsfiddle.net/jayas_godblessall/vz8mcteh/
or execute it here to see :
function toolsQueryResult() {
const query = `query{......}`
return request('http://...', query, ).then(data => {
return data
})
}
// just to emulate your api call
function request(foo) {
return Promise.resolve(["item1", "item2"]);
}
var toolsQueryResult = toolsQueryResult();
var toolsNames = [];
toolsQueryResult.then(function(result) {
result.forEach(function(item) {
toolsNames.push(item)
})
console.log("am executed after you waited for promise to complete - in this case successfully, so you can see the tool sets")
console.log(toolsNames)
})
console.log("am executed before you could resolve promise")
console.log(toolsNames)

Check for element being present then check if its displayed

I need to check if the pop up exists, if it does then I need to check if its displayed then perform certain action on it.
I have implemented the below. I was wanting to know if there is any better way of achieving this.
licenseUpdate.isPresent().then(function (item) {
if (item == true) {
licenseUpdate.isDisplayed().then(function (res) {
if (res == true){
licenseUpdate.click();
};
});
}
});
If you are using page object (you should) you can write something like this:
clickLicenseUpdate() {
const licenseUpdate = $(licenseUpdateCssSelector);
return licenseUpdate.isPresent()
.then((isPresent) => {
if (!isPresent) { return false; }
return licenseUpdate.isDisplayed();
})
.then((isDisplayed) => {
if (!isDisplayed) { return false; }
return licenseUpdate.click().then(() => true);
})
}
Note that if you are using and old JS version (you shouldn't) you need to replace arrow functions with traditional anonymous functions.
Some helpful links about Page Object Design Pattern:
PageObjects
Martin Fowler PageObject
Code explained (or at least, that is the plan):
Using $ to locate an element but you can use any strategy
supported by Protractor.
browser.findElement(by.className('license')) equivalent to
$('license'), browser.findElement(by.id('license')) equivalent to
$('#license'). Check Protractor documentation for more examples.
Once you a have found a web element that match your locator, you can
use isPresent method to determine whether the element is present on
the page. isPresent returns a promise that resolve to a boolean
value.
then always return a promise. You can return a primitive value from
onFulfilled callback and that value would be cast to a promise with
resolve with same value. That is what is done here: if (!isPresent) { return false; }; or you can return another promise
from onFulfilled callback and the promise returned by then will be resolved or rejected with same value of returned promise.
That is what is done here: return licenseUpdate.isDisplayed();. isDisplayed() also return a
promise that will resolve with whether this element is currently
visible on the page.
This can be a bit overwhelming if you are not
used to deal with promises. Check this out Promises/A+
Finally, if the element is present and is displayed, click the element with theclick method that, surprise, also return a
promise (WebDriverJS API is based on promises).
Note that if element is not present, isPresent is false in this
line if (!isPresent), returning false immediately bypass
licenseUpdate.isDisplayed() execution and resolve with a false value. In that
case isDisplayed value is false and again false is returned
immediately bypassing the licenseUpdate.click() execution.
Also note that clickLicenseUpdate return a promise that will
resolve to false if the element is not present or if is present but not
displayed. To keep clickLicenseUpdate returned value consistent, I recommend you to wait for licenseUpdate.click() and then return a boolean value as it is done here: return licenseUpdate.click().then(() => true); (using implicit return from arrow functions) because promise returned by click() resolve with a void value.
That is harmless but is considered a good practice maintain a consistent return value, always a boolean value, not sometime a boolean and others a void value.

Resources