How can I get error message of weatherapi using fetch - javascript

I'm unable to get an error response for the web app, it displays if there's a valid response but doesn't display anything on a bad request. How can I get the error message either on the console or as json string
Here's the code below
const getWeather = () => {
let city = document.querySelector("input").value;
fetch(
`http://api.weatherapi.com/v1/current.json?key=ca7ec552bc034514a9792135211812&q=${city}&aqi=no`
)
.then((data) => data.json())
.then((data) => displayWeather(data));
};
document.querySelector("button").addEventListener("click", () => {
getWeather();
document.querySelector("input").innerText = "";
});
const displayWeather = (data) => {
const localInfo = data.location.localtime;
const name = data.location.name;
const icon = data.current.condition.icon;
const text = data.current.condition.text;
const temp = data.current.temp_c;
const humidity = data.current.humidity;
const country = data.location.country;
const windSpeed = data.current.wind_kph;
const code = data.current.condition.code;
const error =data.error.message;
console.log(name, icon, text, temp, humidity, country, windSpeed, code, error);
Live code at this time of writing this: https://github.com/samuelajala01/my-weather-app/blob/master/script.js

The weather API will only send error sub object if there is error, otherwise it will just send current and location sub objects, hence you just had to add a check for existence of error object in response data
Something like this :
document.querySelector("button").addEventListener("click", () => {
getWeather();
document.querySelector("input").innerHTML = " ";
});
const displayWeather = (data) => {
if (data.error) {
alert(data.error.message);
} else {
const localInfo = data.location.localtime;
const name = data.location.name;
const icon = data.current.condition.icon;
const text = data.current.condition.text;
const temp = data.current.temp_c;
const humidity = data.current.humidity;
const country = data.location.country;
const windSpeed = data.current.wind_kph;
const code = data.current.condition.code;
console.log(name, icon, text, temp, humidity, country, windSpeed, code);
}
}
const getWeather = () => {
let city = document.querySelector("input").value;
fetch(
`http://api.weatherapi.com/v1/current.json?key=ca7ec552bc034514a9792135211812&q=${city}&aqi=no`
)
.then((data) => data.json())
.then((data) => displayWeather(data))
};

Related

Getting empty array

I have declared three arrays dronesList, ndzDrones, pilotsList. After all the conditions are executed, I am getting updated data of dronesList and ndzDrones but the pilotsList is empty, although I'm receiving the data and pilotsList is updated inside the condition after pushing but outside of condition getting Empty[] of pilotsList. Can't figure what I'm missing here.
Any help will be appriciated
let dronesList = []
let ndzDrones = []
try {
const xml = await fetch(
"https://assignments.reaktor.com/birdnest/drones"
).then((res) => res.text())
.catch((error) => {
console.error('Error:', error)
})
const jsonData = parser.parse(xml)
const dronesData = jsonData.report.capture.drone;
dronesList = dronesData;
if(dronesList !== ""){
dronesList.map((drone) =>{
const x = Number(drone.positionX)
const y = Number(drone.positionY)
if(checkVoilation(x, y)){
ndzDrones = ndzDrones.concat(drone)
}
})
}
let pilotsList = []
if(ndzDrones !== ""){
ndzDrones.forEach(async (drone) => {
const serial = drone.serialNumber
const response = await fetch(`https://assignments.reaktor.com/birdnest/pilots/${serial}`)
const data = await response.json()
pilotsList = pilotsList.concat(data)
})
}
console.log(dronesList) //Output [drones]
console.log(ndzDrones) //Output [ndzDrones]
console.log(pilotsList) //Output [] Empty Array
res.status(200).json(pilotsList)
} catch (error) {
res.status(404).json({message: error.message})
}
const pilotsList = await Promise.all(ndzDrones.map(async (drone) => {
const serial = drone.serialNumber
const response = await fetch(`https://assignments.reaktor.com/birdnest/pilots/${serial}`)
const data = await response.json()
return data
})).flat()

How can I download an array of URLs synchronously to maintain their order in React Native?

I'm using React Native and Firebase V9. I am trying to download URLs from storage, but I'm not able to both download all the URLs in the correct order and have them load the first time I visit the screen. I know that getDownLoadURL() runs asynchronously, which is why the images get downloaded in random order each time. There is something I'm not understanding correctly.
Here are two main ways I've tried:
const [memNameLogs, setMemNameLogs] = useState([]);
const [memIDLogs, setMemIDLogs] = useState([]);
const [memImagesLogs, setMemImagesLogs] = useState([]);
const [memberCount, setMemberCount] = useState(0);
const getGroupInfo = async () => {
let memberIDs = [];
let memberNames = [];
let memberImages = [];
let userGroupsRef = collection(db, "groups", groupID, "members");
onSnapshot(userGroupsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
memberIDs.push(document.id);
onSnapshot(doc(db, "users", document.id), (snapshot) => {
const one = snapshot.data();
const two = one.firstName;
const three = one.lastName;
const four = two + ' ' + three;
memberNames.push(four);
console.log(memberNames);
});
const pathReference = ref(storage, 'userProfileImage/' + document.id);
// Get the download URL
getDownloadURL(pathReference)
.then((url) => {
memberImages.push(url);
})
});
setMemberCount(memberIDs.length);
setMemIDLogs(memberIDs);
setMemNameLogs(memberNames);
setMemImagesLogs(memberImages);
})
and
const getGroupMembers = async () => {
let memberIDs = [];
let memberNames = [];
let paths = [];
let userGroupsRef = collection(db, "groups", groupID, "members");
onSnapshot(userGroupsRef, (querySnapshot) => {
querySnapshot.forEach((document) => {
memberIDs.push(document.id)
onSnapshot(doc(db, "users", document.id), (snapshot) => {
const one = snapshot.data();
const two = one.firstName;
const three = one.lastName;
const four = two + ' ' + three;
memberNames.push(four);
});
const pathReference = ref(storage, 'userProfileImage/' + document.id);
paths.push(pathReference);
})
setMemberCount(memberIDs.length);
setMemIDLogs(memberIDs);
setMemNameLogs(memberNames);
setImagePathReferences(paths);
})
return Promise.all(imagePathReference)
}
const getGroupPictures = async (file) => {
let downloadURL;
await getDownloadURL(file)
.then((url) => {
downloadURL = (url);
})
.catch((error) => {
// Handle any errors
if (error.code === "storage/object-not-found") {
downloadURL = ('');
}
});
return (downloadURL)
}
const getGroupInfo = async () => {
const references = await getGroupMembers();
console.log(references);
for (let i = 0; i < memberCount; i++) {
let file = references[i];
const references2 = await getGroupPictures(file);
memImagesLogs.push(references2);
}
};
A simple fix is to store the download URLs in an associative array, with the file as the key.
Something like this:
const downloadURLs = {}; // 👈
const getGroupInfo = async () => {
const references = await getGroupMembers();
console.log(references);
for (let i = 0; i < memberCount; i++) {
const file = references[i];
const downloadURL = await getGroupPictures(file);
downloadURLs[file] = downloadURL; // 👈
}
};
With that, the references array determines the order, while the downloadURLs object allows you to look up the download URL for each file.

unsplash query gives random image

I'm new in learning of building apps with API and node.js and stuck with a problem.
UI is updated from city.value input and when I fetch API, search query return a random img, however when I hardcode value in fetch url the image is correct.
Help please, what I'm missing/not seeing?
const city = document.getElementById('city').value;
const messageBody = document.getElementById('content');
const postImage = new Image();
fetch(`https://api.unsplash.com/search/photos?query=${city}&client_id=${client_id}`)
.then(res => res.json())
.then(
result => {
const imgurl = result.results.map(
hit => hit.urls.regular
);
postImage.src = imgurl[0];
console.log(imgurl[0])
},
error => {
console.log(error);
}
);
const updateUI = (items) => {
let html = items.map((item => {
return `
<p>
${item.content}
<img src='${postImage.src}' width='400'>
</p>`;
})).join(" ");
messageBody.innerHTML = html;
}

Copy a document into a new collection in firestore using cloud function

Path 1 - Match_Creator/cricket/matchList;
Path 2 - Match_Creator/cricket/completedMatchList;
I have a collection called matchList (Path 1) In which i am having a doc called c434108.
Now I want to move this doc(c434108) to Path 2;
/* eslint-disable promise/catch-or-return */
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { db } = require("./db/index");
const createCompletedMatchListDoc = (request, response) => {
completedMatchDocsData();
};
function completedMatchDocsData() {
createNewCompletedMatchDocs()
}
function getOldCompletedMatchDocs(){
var completedMatchesRef = db
.collection("Match_Creator")
.doc("cricket")
.collection("matchList");
var completedMatchDocData;
var completedMatchDataArr = [];
return new Promise(resolve => {
let query = completedMatchesRef
.where("status", "==", "live")
.get()
.then(snapshot => {
// eslint-disable-next-line promise/always-return
if (snapshot.empty) {
console.log("No matching documents.");
return;
}
snapshot.forEach(doc => {
completedMatchDocData = doc.data();
completedMatchDataArr.push(completedMatchDocData);
resolve(completedMatchDataArr);
});
console.log("sarang", completedMatchDataArr[2]);
})
.catch(err => {
console.log("Error getting documents", err);
});
});
}
const createNewCompletedMatchDocs = (async(change, context) => {
let completedMatchData = await getOldCompletedMatchDocs();
console.log('aman', completedMatchData[1]);
const newValue = change.after.data();
const previousValue = change.before.data();
const st1 =newValue.status;
const st2 = previousValue.status;
console.log('I am a log entry' + st1 + ' ' + st2);
var data = completedMatchData[0];
return db.collection('Match_Creator').doc('cricket').collection('completedMatchList').add(data)
.catch(error => {
console.log('Error writting document: ' + error);
return false;
});
})
module.exports = createCompletedMatchListDoc;
And After copy this doc(c434108) i want to delete this doc(c434108) from path 1.
And My index.js file is:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const storeMatchData = require("./liveScoring");
const createCompletedMatchListDoc = require("./completedMatchList");
var http = require("https");
module.exports = {
liveScoring: functions.https.onRequest(storeMatchData),
createCompletedMatchListDoc: functions.https.onRequest(
createCompletedMatchListDoc
)
};
I am able to solve my problem.
This is my completeMatchList.js file
/* eslint-disable promise/catch-or-return */
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { db } = require("./db/index");
const createCompletedMatchListDoc = (request, response) => {
completedMatchDocsData();
};
function completedMatchDocsData() {
setNewCompletedMatchDocs()
}
function getOldCompletedMatchDocs(){
var completedMatchesRef = db
.collection("Match_Creator")
.doc("cricket")
.collection("matchList");
var completedMatchDocData;
var completedMatchDataArr = [];
return new Promise(resolve => {
let query = completedMatchesRef
.where("status", "==", "live")
.get()
.then(snapshot => {
// eslint-disable-next-line promise/always-return
if (snapshot.empty) {
console.log("No matching documents.");
return;
}
snapshot.forEach(doc => {
// completedMatchDocData = doc.data();
completedMatchDocData = {
docId: "",
docData: ""
}
completedMatchDocData.docId = doc.id;
completedMatchDocData.docData = doc.data();
completedMatchDataArr.push(completedMatchDocData);
resolve(completedMatchDataArr); // Here i am getting the data and pushing it in array
});
console.log("sarang", completedMatchDataArr);
})
.catch(err => {
console.log("Error getting documents", err);
});
});
}
const setNewCompletedMatchDocs = (async () => {
let getCompletedMatchData = await getOldCompletedMatchDocs();
// console.log("balram", getCompletedMatchData[0].docId);
let newCompletedMatchDocRef = db.collection("Match_Creator").doc("cricket").collection("completedMatchList").doc(getCompletedMatchData[0].docId);
return newCompletedMatchDocRef.set(getCompletedMatchData[0].docData); //set/copy the data to new path.
})
This is my main index.js file
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const storeMatchData = require("./liveScoring");
const createCompletedMatchListDoc = require("./completedMatchList");
const { db } = require("./db/index");
var http = require("https");
module.exports = {
liveScoring: functions.https.onRequest(storeMatchData),
createCompletedMatchListDoc: functions.https.onRequest(
createCompletedMatchListDoc
)
};
Now after copy document data to a new path i will delete the previous document. For deleting the document i have not written the function.
I'm not seeing anything that would allow you to move a document between collections(someone correct me if I'm wrong). You have to copy from the old collection to the new one and then remove the old one.
This is another post on StackOverflow that is running into this same issue and someone provided Java code on how to implement it.
EDIT: Updated link.
Hope this helps.

Receiving Promise {<pending>} back from .then()

I have an API call in api.js:
export const getGraphData = (domain, userId, testId) => {
return axios({
url: `${domain}/api/${c.embedConfig.apiVersion}/member/${userId}/utests/${testId}`,
method: 'get',
});
};
I have a React helper that takes that data and transforms it.
import { getGraphData } from './api';
const dataObj = (domain, userId, testId) => {
const steps = getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
console.log(steps);
// const steps = test.get('steps');
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) { elemName = element.get('caption'); }
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
};
export default dataObj;
The issue is steps, when logged outside the .then() returns a Promise {<pending>}. If I log results.attributes inside the .then(), I see the data fully returned.
You need to wait until your async call is resolved. You can do this by chaining on another then:
getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
})
.then(steps => {
// put the rest of your method here
});
You can also look at async/await if your platform supports it which would allow code closer to your original
const steps = await getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
// can use steps here
You have 2 options to transform your fetched data :
1st option : create a async function that returns a promise with the modified data :
const dataObj = (domain, userId, testId) => {
return getGraphData(domain, userId, testId).then((result) => {
const steps = result.attributes;
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) {
elemName = element.get('caption');
}
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
});
};
With es7 async/await syntax it should be :
const dataObj = async (domain, userId, testId) => {
const result = await getGraphData(domain, userId, testId);
const steps = result.attributes;
... modify the data
}
Then keep in mind that this function returns a promise, you'll need to wait for it to get the result, example in a react component :
componentDidMount(){
dataObj('mydomain', 'myuserId', 'mytestId').then((res) => {
this.setState({ data: res });
}
}
The component will update when the promise is resolve, you can then use the data (you'll need to handle the undefined data state in render method)
2nd option : Create a sync function to modify the data :
const dataObj = (steps) => {
const expr = /select/;
const resultsSteps = [];
steps.forEach((step) => {
...
}
return newResultsSteps;
};
To have the same result as option 1 in our component we'll use it like this :
componentDidMount(){
getGraphData('mydomain', 'myuserId', 'mytestId').then((res) => {
const modifiedData = dataObj(res);
this.setState({ data: modifiedData });
}
}
That's how promises work. The data is not ready when you are trying to use it so you should move all your processing into the .then. The reason your variable is a Promise {<pending>} is because you can chain other things onto it.
Something like:
steps.then((steps) => {
...
});

Categories