Accessing the JSON fulfillmentValue value - javascript

I'm new to Vue and I'm using electron-vue boilerplate. I am trying to display some items in Amazon using Promise in NodeJS. I'm using "amazon-product-api" and yes, I have access to the Amazon Product Advertising API. I have written my code in a separate .js file and have linked in Vue component.
This is my function to get the ItemSearch using amazon-product-api,
// This function will take take idea and give stats in return
// #input string
// #return json
function getKeywordStats (keyword, searchType, delay, domain) {
// Setting URL and headers for request
if (searchType === 0) {
searchType = 'KindleStore'
} else {
searchType = 'Books'
}
if (domain === 'com') {
domain = 'webservices.amazon.com'
} else if (domain === 'de') {
domain = 'webservices.amazon.de'
} else if (domain === 'uk') {
domain = 'webservices.amazon.uk'
}
var query = {
keywords: keyword,
searchIndex: searchType,
sort: 'relevancerank',
itemPage: 1,
availability: 'Available',
responseGroup: 'Large',
domain: domain
}
return new Promise(function (resolve, reject) {
amazonClient.itemSearch(query, function (error, results) {
if (error) {
console.log(error)
} else {
var title = results[0].ItemAttributes[0].Title[0]
var imageUrl = results[0].ItemAttributes[0].Title[0]
var data = {
title: title,
imageUrl: imageUrl
}
// console.log(results)
// var noCompetitors = results.ItemAttributes.Items.TotalResults
resolve(data)
}
})
})
}
This is my Vue component,
<template>
<div class='hello'>
<p> {{data}} </p>
</div>
</template>
<script>
/* eslint-disable no-unused-vars */
// ebook data - 0
// book data - 1
var amazon = require('../../controllers/amazon-service.js')
var keywordDictionary = {}
var getStats = amazon.getKeywordStats('trump aftershock', 0, null, 'de')
console.log()
export default {
data () {
return {
data: [
{
terms: getStats
}
]
}
}
}
</script>
<style>
.hello {
color: blue
}
</style>
When I run this I'm able to show the promise data in my Vue component. This is how it showed on the page.
[ { "terms": { "isFulfilled": true, "isRejected": false, "fulfillmentValue": { "title": "Trump Aftershock: The President's Seismic Impact on Culture and Faith in America (English Edition)", "imageUrl": "Trump Aftershock: The President's Seismic Impact on Culture and Faith in America (English Edition)" } } } ]
But I wasn't able to get the "fulfillmentValue". I will attach some screenshots which will help to solve this issue. If I am doing it wrong please guide me to the right path.
What th app shows -
Verified the JSON response -

The Amazon API function getKeywordStats is returning a Promise – it's an asynchronous operation. This means the function doesn't return the result like a normal synchronous function, instead it returns a Promise object which you can then register a callback function (via then) to be called once the data is retrieved. Be sure to read up on how promises work; I won't go into detail here since there's lots of information about them already.
Move the API call into your component created hook:
export default {
data() {
return {
terms: null
}
},
created() {
amazon.getKeywordStats('trump aftershock', 0, null, 'de').then(terms => {
this.terms = terms
})
}
}

Related

Passing in a result of a function to generate readme

Bootcamp student here. I seem to be having trouble passing in the result of function renderBadge(license) to the generateREADME function. I am using inquirer to grab inputs, and generate a readme. The functions renderBadge() and licenseLink() is solely pertaining to license portion of the inquirer. However, I can't seem to pass this info along and display it to the generating function. Is there a way to do this? What am I doing wrong? Thanks in advance.
Upon function execution, the ${badge} seems to be undefined.
const inquirer = require("inquirer");
const fs = require("fs");
const generateREADME = ({ title, description, installation, usage, contributions, tests, license, github, email, badge,}) =>
`# ${title}
${badge}
## Description
${description}
(Rest of Readme Generation here)
`
inquirer
.prompt([
{
(other prompts here)
},
{
type: "list",
name: "license",
message: "What license is your project?",
choices: [
"Apache 2.0",
"Boost",
"GNU AGPL v3",
"MIT",
"Perl",
"other",
],
validate: (licenseInput) => {
if (licenseInput) {
return true;
} else {
console.log(`Please enter your project's license!`);
return false;
}
},
}
])
.then((answers) => {
const readmePageContent = generateREADME(answers);
renderBadge(answers)
fs.writeFile('README.md', readmePageContent, (err) => {
err ? console.log(err) : console.log('Successfully generated README!')
})
})
function renderBadge(license) {
let badge = ''
if (license === 'Apache 2.0') {
badge = `![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)]`
} else if (license === 'Boost') {
badge = `![License](https://img.shields.io/badge/License-Boost_1.0-lightblue.svg)]`
} else if (license === 'GNU APGL v3') {
badge = `![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)]`
} else if (license === 'MIT') {
badge = `![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)]`
} else if (license === 'Perl') {
badge = `![License: Artistic-2.0](https://img.shields.io/badge/License-Perl-0298c3.svg)]`
} else {
badge = ''
}
return badge;
generateREADME(badge)
}
The main issue here is the way you pass/accept arguments.
answers is an object containing the key/value-pairs an example could be:
const answers = {
title: "Hello World",
description: "Hello World, this is a test description",
// ...
license: "GNU AGPL v3",
};
You then pass the answers object to renderBadge.
renderBadge(answers)
However in renderBadge you expect license as the sole argument.
function renderBadge(license) {
// ...
}
Since you passed the whole answers object, that is what you will receive. Meaning that the licence parameter will contain the answers object.
To fix this you should pass just the license to renderBadge, not all the answers. So use renderBadge(answers.license) instead.
Alternatively you could also use object destructuring like you did in generateREADME, and define renderBadge as:
function renderBadge({ license }) {
// ...
}
If you choose to use object destructuring, you should still pass the full answers object to renderBadge, so renderBadge(answers).
The second, non-essential mistake is:
return badge;
generateREADME(badge) // never executed
The line after the return is never executed. This doesn't really break anything, since you didn't need that line anyways, so it can just be removed.
Lastly, and probably most importantly the order of the following lines are incorrect.
const readmePageContent = generateREADME(answers);
renderBadge(answers.license) // after the first fix
The renderBadge() call should be made before you render the readme file, the resulting contents should then be passed as argument to generateREADME().
const badge = renderBadge(answers.license);
const readmePageContent = generateREADME({ ...answers, badge });
This uses the spread syntax in object literals combined with the property definition shorthand to pass a single object, containing al the required arguments.
So the final result might look like this (with minimum changes):
const inquirer = require("inquirer");
const fs = require("fs");
const generateREADME = ({title, description, installation, usage, contributions, tests, license, github, email, badge,}) => (
`# ${title}
${badge}
## Description
${description}
(Rest of Readme Generation here)
`
);
inquirer.prompt([
{
(other prompts here)
},
{
type: "list",
name: "license",
message: "What license is your project?",
choices: [
"Apache 2.0",
"Boost",
"GNU AGPL v3",
"MIT",
"Perl",
"other",
],
validate: (licenseInput) => {
if (licenseInput) {
return true;
} else {
console.log(`Please enter your project's license!`);
return false;
}
},
}
]).then((answers) => {
const badge = renderBadge(answers.license); // pass only the license, not all the anwers
const readmePageContent = generateREADME({ ...answers, badge }); // pass the answers combined with the badge
fs.writeFile('README.md', readmePageContent, (err) => {
err ? console.log(err) : console.log('Successfully generated README!')
})
});
function renderBadge(license) {
let badge = ''
if (license === 'Apache 2.0') {
badge = `![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)]`
} else if (license === 'Boost') {
badge = `![License](https://img.shields.io/badge/License-Boost_1.0-lightblue.svg)]`
} else if (license === 'GNU APGL v3') {
badge = `![License: AGPL v3](https://img.shields.io/badge/License-AGPL_v3-blue.svg)]`
} else if (license === 'MIT') {
badge = `![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)]`
} else if (license === 'Perl') {
badge = `![License: Artistic-2.0](https://img.shields.io/badge/License-Perl-0298c3.svg)]`
} else {
badge = ''
}
return badge; // removal of generateREADME
}
When you put a return statement in a function, all the code below it doesn't execute.
In your renderBadge function, you invoke generateREADME(badge) after the return statement. So it never runs:
function renderBadge(license) {
let badge = ''
...
} else {
badge = ''
}
return badge;
generateREADME(badge) // this line doesn't execute
}
To call generateREADME() with the renderBadge function output, you need to remove the generateREADME(badge) statement:
function renderBadge(license) {
let badge = ''
...
} else {
badge = ''
}
return badge;
}
After that, call the function in the relevant place and store the output in the variable like so:
...
.then((answers) => {
const readmePageContent = generateREADME(answers);
input = renderBadge(answers)
generateREADME(input)
Or you can do it succinctly like this:
...
generateREADME(renderBadge(answers))
...

Pinia|Vue3 I can't access the property of the object that returned from the Pinia action

first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']

Construct MongoDB query from GraphQL request

Let's say we query the server with this request, we only want to get the following user's Email, My current implementation requests the whole User object from the MongoDB, which I can imagine is extremely inefficient.
GQL
{
user(id:"34567345637456") {
email
}
}
How would you go about creating a MongoDB filter that would only return those Specified Fields? E.g,
JS object
{
"email": 1
}
My current server is running Node.js, Fastify and Mercurius
which I can imagine is extremely inefficient.
Doing this task is an advanced feature with many pitfalls. I would suggest starting building a simple extraction that read all the fields. This solution works and does not return any additional field to the client.
The pitfalls are:
nested queries
complex object composition
aliasing
multiple queries into one request
Here an example that does what you are looking for.
It manages aliasing and multiple queries.
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify({ logger: true })
const schema = `
type Query {
select: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
select: async (parent, args, context, info) => {
const currentQueryName = info.path.key
// search the input query AST node
const selection = info.operation.selectionSet.selections.find(
(selection) => {
return (
selection.name.value === currentQueryName ||
selection.alias.value === currentQueryName
)
}
)
// grab the fields requested by the user
const project = selection.selectionSet.selections.map((selection) => {
return selection.name.value
})
// do the query using the projection
const result = {}
project.forEach((fieldName) => {
result[fieldName] = fieldName
})
return result
},
},
}
app.register(mercurius, {
schema,
resolvers,
graphiql: true,
})
app.listen(3000)
Call it using:
query {
one: select {
a
}
two: select {
a
aliasMe:b
}
}
Returns
{
"data": {
"one": {
"a": "a"
},
"two": {
"a": "a",
"aliasMe": "b"
}
}
}
Expanding from #Manuel Spigolon original answer, where he stated that one of the pitfalls of his implementation is that it doesn't work on nested queries and 'multiple queries into one request' which this implementation seeks to fix.
function formFilter(context:any) {
let filter:any = {};
let getValues = (selection:any, parentObj?:string[]) => {
//selection = labelSelection(selection);
selection.map((selection:any) => {
// Check if the parentObj is defined
if(parentObj)
// Merge the two objects
_.merge(filter, [...parentObj, null].reduceRight((obj, next) => {
if(next === null) return ({[selection.name?.value]: 1});
return ({[next]: obj});
}, {}));
// Check for a nested selection set
if(selection.selectionSet?.selections !== undefined){
// If the selection has a selection set, then we need to recurse
if(!parentObj) getValues(selection.selectionSet?.selections, [selection.name.value]);
// If the selection is nested
else getValues(selection.selectionSet?.selections, [...parentObj, selection.name.value]);
}
});
}
// Start the recursive function
getValues(context.operation.selectionSet.selections);
return filter;
}
Input
{
role(id: "61f1ccc79623d445bd2f677f") {
name
users {
user_name
_id
permissions {
roles
}
}
permissions
}
}
Output (JSON.stringify)
{
"role":{
"name":1,
"users":{
"user_name":1,
"_id":1,
"permissions":{
"roles":1
}
},
"permissions":1
}
}

Filter API response directly in URL

I would like to know if it is possible to filter the response of an API directly via the URL.
URL API : https://coronavirus-19-api.herokuapp.com/countries
I only put 2 countries for the example but the structure of the answer is like this:
[
{
"country":"USA",
"cases":176518,
"todayCases":12730,
"deaths":3431,
"todayDeaths":290,
"recovered":6241,
"active":166846,
"critical":3893,
"casesPerOneMillion":533,
"deathsPerOneMillion":10,
"firstCase":"\nJan 20 "
},
{
"country":"Italy",
"cases":105792,
"todayCases":4053,
"deaths":12428,
"todayDeaths":837,
"recovered":15729,
"active":77635,
"critical":4023,
"casesPerOneMillion":1750,
"deathsPerOneMillion":206,
"firstCase":"\nJan 29 "
}
]
For the moment in my project I collect all the responses and I filter afterwards to have only the data for a country but to optimize performance I would like to filter the responses directly by URL.
async getCountryStats() {
try {
let response = await fetch("https://coronavirus-19-api.herokuapp.com/countries")
if (response.status === 200) {
let data = await response.json()
// Data object with ID
data = Object.assign({}, data)
// Data object with name of property
let obj = {}
for (let i in data) {
obj = { ...obj, [data[i].country]: data[i] }
}
this.setState({ focusStats: obj[this.state.focusCountry] })
} else {
this.setState({ errorStatus: true })
console.error('Error status')
}
} catch (err) {
console.error(err)
}
}
I use React, here is my repository: https://github.com/michaelbaud/covid19, here is the rendering: https://suspicious-kilby-d90f99.netlify.com
You can use the following link instead:
https://coronavirus-19-api.herokuapp.com/countries/{country-name}
For example in your case it would be:
USA : https://coronavirus-19-api.herokuapp.com/countries/USA
Italy : https://coronavirus-19-api.herokuapp.com/countries/italy
Good Luck

Conditional get request in vue for rendering a subcomponent scoped

When I click a profile (of an author) component, I can't figure out how it should render a scoped sub-component, listing the main entities of the app, so-called fabmoments (containers for 3D print information).
My current solution looks like this:
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
// search: '',
localAuthor: '',
fabmoments: []
}
},
created () {
this.localAuthor = this.author
if (typeof localAuthor !== 'undefined') {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
This renders all in the profile, where it should render a list scoped to the current profile's author.
And it renders nothing in the home (without receiving the prop), where it should render all.
I am not much of star in JavaScript. What am I doing wrong?
UPDATE
This works as a solution, though not very elegant.
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
fabmoments: []
}
},
created () {
if (this.author.id >= 0) {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
Not sure which part is wrong, but you may definitely debug your code to find out why fabmoments is empty array assuming there is no error occurred yet.
There are three parts to debug:
http response -- to check if data is properly returned
this -- to check if this pointer still points at the component
template -- to check if fabmoments are correctly bind to the element
At last, it would be better to separate your http request logics from your components.
Good luck!

Categories

Resources