Passing in a result of a function to generate readme - javascript

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))
...

Related

Am trying to update a database in sequelize

I am trying to update a sequelize database, where the fields that need to be updated is optional. The problem is that I have 3 fields that need to be updated which are all optional. I do not want to check each field one by one calling update method. Cause that will mean multiple call to the api. Sample raw body input in JSON
{
"authorIds": [1, 5],
"tags": ["tech", "health"],
"text": "Some very short blog post text here."
}
Any of these fields can be optional. This is what I have so far
const { authorIds, tags, text } = req.body;
// case where all fields came in
if (authorIds && tags && text) {
try {
const ids = authorIds.join(',');
const tagValue = tags.join(',');
await Post.update(
{ authorIds: ids, tags: tagValue, text: text },
{ where: { id: postId } }
);
} catch (error) {
res.json({ error: 'Please check your body format' });
}
}
Note I am using SQLite, so I can not store arrays, that why am making the inputs. into string
Thanks
You can easily construct an object that you need to pass as the first argument to update dynamically:
if (authorIds || tags || text) {
try {
const fieldsToUpdate = {}
if (authorIds && authorIds.length) {
const ids = authorIds.join(',');
fieldsToUpdate.authorIds = ids;
}
if (tags && tags.length) {
const tagValue = tags.join(',');
fieldsToUpdate.tags = tagValue;
}
if (text) {
fieldsToUpdate.text = text;
}
await Post.update(
fieldsToUpdate,
{ where: { id: postId } }
);
} catch (error) {
res.json({ error: 'Please check your body format' });
}
}
Also you can try to use object deconstruction along with ternary operators to combine all fields right in the update call.
...(authorIds && authorIds.length ? { authorIds: authorIds.join(',') } : {}).

How to add a field to a GraphQL document?

Let's say this is my graphql query:
mutation Test ($input: UpdateUserAccountInput!) {
updateUserAccount(input: $input) {
... on UpdateUserAccountPayload {
userAccount {
name
}
}
}
}
I want to modify to have the following fragment:
... on Error {
message
}
I was able to figure out that I can get AST using parse from graphql package, i.e.
import {
parse,
gql,
} from 'graphql';
parse(gql`
mutation Test ($input: UpdateUserAccountInput!) {
updateUserAccount(input: $input) {
... on UpdateUserAccountPayload {
userAccount {
name
}
}
}
}
`)
Now I am trying to figure out how to add ... on Error { message } to this query.
The problem that I am trying to solve is that my tests sometimes quietly fail because mutation returns an error that I did not capturing. I am extending my GraphQL test client to automatically request errors for every mutation and throw if error is returned.
I assume there exists some utilities that allow me to inject fields into AST, but so far I was not able to find them.
I think I figured it out.
Here is my solution:
const appendErrorFieldToMutation = (query: string) => {
const inlineErrorFragmentTemplate = {
kind: 'InlineFragment',
selectionSet: {
kind: 'SelectionSet',
selections: [
{
kind: 'Field',
name: {
kind: 'Name',
value: 'message',
},
},
],
},
typeCondition: {
kind: 'NamedType',
name: {
kind: 'Name',
value: 'Error',
},
},
};
const document = parseGraphQL(query);
if (document.definitions.length !== 1) {
throw new Error('Expected only one definition');
}
const definition = document.definitions[0] as OperationDefinitionNode;
if (definition.operation !== 'mutation') {
return query;
}
if (definition.selectionSet.selections.length !== 1) {
throw new Error('Expected only one document selection');
}
const documentSelection = definition.selectionSet.selections[0] as InlineFragmentNode;
const errorField = documentSelection.selectionSet.selections.find((selection) => {
return (selection as InlineFragmentNode).typeCondition?.name.value === 'Error';
});
if (!errorField) {
// #ts-expect-error – Intentionally mutating the AST.
documentSelection.selectionSet.selections.unshift(inlineErrorFragmentTemplate);
}
return printGraphQL(document);
};
I've not used any utilities, so perhaps there is a smarter way to do the same.

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
}
}

Assignment of Nuxt Axios response to variable changes response data content

async fetch() {
try {
console.log(await this.$api.events.all(-1, false)); // <-- First log statement
const res = await this.$api.events.all(-1, false); // <-- Assignment
console.log(res); // <-- Second log statement
if (!this.events) {
this.events = []
}
res.data.forEach((event, index) => {
const id = event.hashid;
const existingIndex = this.events.findIndex((other) => {
return other.hashid = id;
});
if (existingIndex == -1) {
this.events.push(events);
} else {
this.events[existingIndex] = event;
}
});
for (var i = this.events.length - 1; i >= 0; --i) {
const id = this.events[i].hashid
const wasRemoved =
res.data.findIndex((event) => {
return event.hashid == id
}) == -1
if (wasRemoved) {
this.events.splice(i, 1)
}
}
this.$store.commit('cache/updateEventData', {
updated_at: new Date(Date.now()),
data: this.events
});
} catch (err) {
console.log(err)
}
}
// The other functions, maybe this somehow helps
async function refreshTokenFirstThen(adminApi, func) {
await adminApi.refreshAsync();
return func();
}
all(count = -1, description = true) {
const func = () => {
return $axios.get(`${baseURL}/admin/event`, {
'params': {
'count': count,
'description': description ? 1 : 0
},
'headers': {
'Authorization': `Bearer ${store.state.admin.token}`
}
});
}
if (store.getters["admin/isTokenExpired"]) {
return refreshTokenFirstThen(adminApi, func);
}
return func();
},
Both log statements are giving slightly different results even though the same result is expected. But this only happens when is use the function in this specific component. When using the same function in other components, everything works as expected.
First data output:
[
{
"name": "First Name",
"hashid": "VQW9xg7j",
// some more correct attributes
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes
}
]
While the second console.log gives the following output:
[
{
"name": "First Name",
"hashid": "zlWvEgxQ",
// some more correct attributes, but this time with reactiveGetter and reactiveSetter
<get hashid()>: reactiveGetter()
​​ length: 0
​​​​ name: "reactiveGetter"
​​​​ prototype: Object { … }
​​​​<prototype>: function ()
​​​<set hashid()>: reactiveSetter(newVal)
​​​​length: 1
​​​​name: "reactiveSetter"
​​​​prototype: Object { … }
​​​​<prototype>: function ()
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes and still without reactiveGetter and reactiveSetter
}
]
As it can be seen, somehow the value of my hashid attribute changes, when assigning the response of the function call.
The next weird behavior happening here, is that the first object where the hashid field changes also gets reactiveGetter and reactiveSetter (but the second object in the array does not get these).
So it looks to me like something is happening with the assignment that I don't know about. Another guess would be that this has something to do with the Vuex store, because I do not change the Vuex tore in the other place where I use the same function.
It is verified that the backend always sends the correct data, as this is dummy data, consisting of an array with two objects with some attributes. So no other data except this two objects is expected.
Can someone explain to me why this behavior occurs?
There are few problems...
Do not use console.log with objects. Browsers tend to show "live view" of object - reference
this.events.findIndex((other) => { return other.hashid = id; }); is wrong, you are using assignment operator (=) instead of identity operator (===). That's why the hashid of the first element changes...

Accessing the JSON fulfillmentValue value

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
})
}
}

Categories

Resources