Splitting Goole-Play-Scraper Results - node.js

I have a React front-end with a Node back-end, and all I'm trying to do is scrape some data from the Google Play Store using Facundo Olano's Google-Play-Scraper.
This is my React front-end...
import React, {Component} from 'react';
import './App.css';
export default class App extends Component {
state = {
title: '',
review: ''
};
componentDidMount() {
this.callApi()
.then(res => this.setState({
title: res.title,
review: res.review
}))
.catch(err => console.log('Error: ', err));
}
callApi = async () => {
const response = await fetch('/gPlay');
const body = await response.json();
if (response.status !== 200) {
throw Error(body.message);
}
return body;
};
render() {
console.log("==================== app log ====================");
console.log("State: ", this.state);
return (
<div className="App">
<p>Title: {this.state.title}.</p>
<p>Review: {this.state.review}.</p>
</div>
);
}
}
This is my Node back-end...
[const express = require('express');
const app = express();
const port = process.env.PORT||5000;
const gPlay = require('google-play-scraper');
const appToScrape = 'com.mojang.minecraftpe';
app.get('/gPlay', (req, res) => {
const gPlayResults = gPlay.app({appId: appToScrape});
console.log('==================== server log ====================');
gPlayResults.then(console.log);
res.send({
title: 'title',
review: 'review',
});
});
app.listen(port, () => console.log(`Listening on port: ${port}`));
This is a small snippet of the results i'm getting...
{
price: 6.99,
free: false,
currency: 'USD',
priceText: '$6.99',
offersIAP: true,
size: 'Varies with device'
}
I'm getting the full results as per the doc's, but I need to be able to split the returned object up to be able to send bits of it back to the front-end, but I can't for the life of me get it to work.
I thought I could just simply do something like gPlayResults.price and have 6.99 returned, but all that comes back is undefined.
Please can someone help, I must be missing something as it surely can't be that hard!?
Thanks xxx

Ok, I worked it out! :)
For anyone who's interested, gPlayResults is being assigned a Promise and not the object I was expecting.
I made a callback using .then and was able to use the result I needed to send a usable object back to the front end.
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const googlePlayApp = require('google-play-scraper');
const appToScrape = 'com.mojang.minecraftpe';
app.get('/gPlay', (req, res) => {
console.log('==================== /gPlay ====================');
const gPlayResults = googlePlayApp.app({appId: appToScrape});
gPlayResults.then(function(result) {
res.send({
appObject: result
})
});
});
app.listen(port, () => console.log(`Listening on port: ${port}`));
On to the next problem...

Related

Fetch only shows when re-rendering or refreshing page

I have a quiz game project where I've done both backend and frontend. In FE I'm fetching clues for the quiz from BE, and everything works except one thing: When playing the quiz for the first time in a while, the clue data that should be fetched doesn't show. Like this:
But when re-rendering the component or refreshing the whole browser it shows perfectly. Like this:
It also shows perfectly while working on the project, so it's only that first time when you haven't been refreshing or re-rendering in a while that it doesn't show. Is there anything to do to make it work smoothly every time? And could the problem be in FE or BE?
This is the component where I'm fetching:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { game } from 'reducers/game';
import swal from 'sweetalert';
import { Mapillary } from 'components/Mapillary/Mapillary';
import { Loading } from 'components/Loading/Loading';
import { OuterWrapper, InnerWrapper } from 'GlobalStyles';
import { ClueWrapper, MapillaryContainer, ClueContainer, SpecialSpan, ClueText, AnotherClueButton } from './Clues.Styles'
export const Clues = () => {
const [games, setGames] = useState([])
const [loading, setLoading] = useState(false);
const [currentClue, setCurrentClue] = useState(0);
const [level, setLevel] = useState(5);
//* Fetching clues
const fetchClues = () => {
console.log('loading');
setLoading(true);
fetch('https://final-project-api-veooltntuq-lz.a.run.app/games')
.then((response) => {
return response.json()
})
.then((response) => {
setGames(response.games)
console.log('data is fetched')
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}
//* Setting current score
const currentScore = useSelector((store) => store.game.score);
const dispatch = useDispatch();
const handleClick = () => {
setCurrentClue(currentClue + 1);
if (currentClue < 4) {
dispatch(game.actions.setScore(currentScore - 1)); //* If clue index < 4 = Set score to -1
setLevel(level - 1);
} else {
dispatch(game.actions.setScore(currentScore === 1)); //* If clue index > 4 = Set score to 1
swal('Time to make a guess!', {
button: 'OK'
});
}
};
useEffect(() => {
fetchClues()
}, [])
const activeClue = games[currentClue];
if (loading) {
return (
<OuterWrapper>
<InnerWrapper>
<Loading />
</InnerWrapper>
</OuterWrapper>)
}
if (currentClue < 5) { //* Stop showing clues after clue 5
return (
<div>
<MapillaryContainer>
{console.log(currentClue)}
<Mapillary width="auto" height="94vh" imageId={currentClue === 0 ? '343242160559702' : currentClue === 1 ? '463849228173207' : currentClue === 2 ? '273852291114652' : currentClue === 3 ? '953489715410448' : currentClue === 4 ? '814918985897976' : ''} />
</MapillaryContainer>
<ClueWrapper>
<ClueContainer>
<SpecialSpan>{level} points</SpecialSpan>
<ClueText>{activeClue && activeClue.gameOne}</ClueText>
<AnotherClueButton type="button" onClick={() => handleClick()}>I need another clue</AnotherClueButton>
</ClueContainer>
</ClueWrapper>
</div>
)
} else {
return (
<div>
<MapillaryContainer>
<Mapillary width="auto" height="94vh" imageId="814918985897976" />
</MapillaryContainer>
</div>
)
}
}
And this is the BE:
import express from "express";
import cors from 'cors'
import mongoose from 'mongoose'
import games from "./data/games.json";
const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/games"
mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true })
mongoose.Promise = Promise
// Defines the port the app will run on. Defaults to 8080, but can be overridden
// when starting the server. Example command to overwrite PORT env variable value:
// PORT=9000 npm start
const Game = mongoose.model("Game", {
id: Number,
gameOne: String,
gameTwo: String
});
if(process.env.RESET_DB) {
const resetDataBase = async () => {
await Game.deleteMany();
games.forEach(singleGame => {
const newGame = new Game(singleGame);
newGame.save();
})
}
resetDataBase();
}
const port = process.env.PORT || 8080;
const app = express();
// Add middlewares to enable cors and json body parsing
app.use(cors());
app.use(express.json());
// Start defining your routes here
app.get("/", (req, res) => {
res.send({
Welcome:
"This is the API for my final project: A quiz game called StreetSmart.",
Routes: [
{
"/games": "Show all games."
}
],
})
});
// Show all games
app.get("/games", async (req, res) => {
const allGames = await Game.find().sort({"id":1})
res.status(200).json({
success: true,
games: allGames
});
});
// Start the server
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});

How to use socketio inside controllers?

I have a application created with Reactjs,Redux,Nodejs,MongoDB. I have created socketio in backend side.
server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const routes = require('./routes/api/routes');
var server = require('http').Server(app);
const io = require('./socket').init(server);
app.use(express.json());
require('dotenv').config();
mongoose.connect(process.env.MONGO_DB).then(console.log('connected'));
const corsOptions = {
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus: 200,
};
app.use(cors(corsOptions));
app.use(routes);
if (
process.env.NODE_ENV === 'production' ||
process.env.NODE_ENV === 'staging'
) {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
io.on('connection', (socket) => {
console.log('New client connected');
socket.on('disconnect', () => {
console.log('Client disconnected');
});
});
server.listen(process.env.PORT || 5000, () => {
console.log('socket.io server started on port 5000');
});
socket.js
let io;
module.exports = {
init: (httpServer) => {
return (io = require('socket.io')(httpServer, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
}));
},
getIO: () => {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io;
},
};
In controller.js I am creating new item to inside mongodb and also using socketio. I am emitting new Item that I created. It looks like that
controller.js
const createTeam = async (req, res) => {
const userId = req.user.id;
const newItem = new Item({
name: req.body.name,
owner: userId,
});
await newItem.save((err, data) => {
if (err) {
console.log(err);
return res.status(500).json({
message: 'Server Error',
});
}
});
await newItem.populate({
path: 'owner',
model: User,
select: 'name',
});
await User.findByIdAndUpdate(
userId,
{ $push: { teams: newItem } },
{ new: true }
);
io.getIO().emit('team', {
action: 'creating',
team: newItem,
});
res.json(newItem);
};
In frontend side I am listening the socketio server with socket.io-client. In my App.js I can see data that come from backend side when I console.log(data). My app working perfect until this stage. I can take the data from socketio, I can see the new client that connected. But when I send the data with dispatch, I app start to add infinite new items to database. Take a look at my App.js
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import { AppNavbar } from './components/AppNavbar';
import { TeamList } from './components/TeamList';
import { loadUser } from './Store/Actions/AuthActions';
import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { io } from 'socket.io-client';
import { addItems } from './Store/Actions/itemActions';
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUser());
const socket = io('http://localhost:5000');
socket.on('team', (data) => {
if (data.action === 'creating') {
dispatch(addItems(data.team));
}
// console.log(data);
});
}, []);
return (
<div className="App">
<AppNavbar />
<TeamList />
</div>
);
}
export default App;
My itemAction in redux side is also like that
export const addItems = (input) => (dispatch, getState) => {
axios
.post('/api/items/createTeam', input, tokenConfig(getState))
.then((res) =>
dispatch({
type: ADD_ITEM,
payload: res.data,
})
)
.catch((err) =>
dispatch(
returnErrors(err.response.data, err.response.status, 'GET_ERRORS')
)
);
};
My problem is how to stop infinite callling of api after implement socketio. How to stop infinite loop efficiently?
Infinite loop is steming from not dispatching "type: ADD_ITEM" once.This issue cause that your itemAction always would dispatch "type: ADD_ITEM" and payload with it when after every fetching then your react would re-render page again and again.
You should get rid of your dispatching action inside of addItems function and dispatch your action only inside of useEffect in App.js file .
Your code snippet should look like this in App.js:
useEffect(() => {
//...
const socket = openSocket('http://localhost:5000');
socket.on('postsChannel', (data) => {
if (data.action === 'creating') {
dispatch({ type: ADD_ITEM, payload: data.team });
}
});
}, [dispatch]);

I can't get a data from backend/data file

please can someone help me to get the data of products from the backend/data by using Axios.get method to retrieve with the helping of redux store.I do all the steps to get the contain of products but the console send me the initial state empty object so there are all my files :
productActions.js :
import Axios from 'axios';
const PRODUCT_LIST_REQUEST = "PRODUCT_LIST_REQUEST";
const PRODUCT_LIST_SUCCESS = "PRODUCT_LIST_REQUEST";
const PRODUCT_LIST_FAIL = "PRODUCT_LIST_FAIL";
export const listProducts =
() => async (dispatch) => {
dispatch({
type: PRODUCT_LIST_REQUEST
});
try {
const {data} = await Axios.get('/api/products');
dispatch({type: PRODUCT_LIST_SUCCESS, payload: data});
} catch(error) {
dispatch({type: PRODUCT_LIST_FAIL, payload: error.message});
}
}
ProductReducers.js:
const {PRODUCT_LIST_REQUEST} = require('../actions/productActions.js');
const {PRODUCT_LIST_SUCCESS} = require('../actions/productActions.js');
const {PRODUCT_LIST_FAIL} = require('../actions/productActions.js')
export const productListReducer = (state = {loading: true, products: {}}, action) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return {loading: true};
case PRODUCT_LIST_SUCCESS:
return ({loading: false, products: action.payload});
case PRODUCT_LIST_FAIL:
return ({loading: false, error: action.payload});
default:
return state;
}
};
this store.js:
import {createStore, compose, applyMiddleware, combineReducers} from 'redux';
import thunk from 'redux-thunk';
import {productListReducer} from './reducers/productReducers';
const initialState = {};
const reducer = combineReducers({
productList: productListReducer
})
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, initialState, composeEnhancer(applyMiddleware(thunk)));
export default store;
server.js :
import express from 'express';
import {data} from './data.js';
const app = express();
app.get('/api/products', (req, res) => {
res.send(data.categories)
});
app.get('/', (req, res) => {
res.send('server is ready')
});
const port = process.env.port || 5001;
app.listen(port, ()=> {
console.log(`server is runing at http://localhost:${port}`);
})
I am not sure but, I think you must return in your switch cases like this:
return {...state, loading: false, products: action.payload};
Just try all of them like this. And try to console logging the data you get from axios.get. I am pretty sure that you must use JSON.parse(data) before setting it to your payload.

ctx request body is undefined with nodejs

I have a problem, when I post my form, i can't get what is in my post in my API.
this is my post with axios in react :
onSubmit = () => {
let data = {
nickname: this.state.nickname,
password: this.state.password
}
axios.post("/api/user/login", { data })
.then(res => {
console.log("res from api is => ", res.data);
})
}
and this is in my API :
import koaBody from "koa-body";
const app = new koa();
const router = new Router();
app.use(router.routes());
app.use(koaBody());
router.post("/api/user/login", async(ctx) => {
console.log("body is => ", ctx.request.body);
ctx.body = "ok";
});
the problem is ctx.request.body is always undefined...
Can you tell me why ?
I tried with router.get and I have no problem, it works fine.
You need to load the body parser before the router, otherwise the router will get to handle the requests before the body contents are parsed:
app.use(koaBody());
app.use(router.routes());

I'm trying to use Stripe payment with my React-app and Node server

I'm a self-taught programmer with a lot to learn :)
I'm building a website for my first customer and I'm trying to implement the Stripe payment platform. I am building with React.js express and Node. I've been using this article for most of the setup: https://www.robinwieruch.de/react-express-stripe-payment/
On my test API, everything works absolutely perfect. I can see the test data with payments and everything. When I'm on the live website with the live API keys it doesn't create a charge. It posts the API call and has all the correct information but it doesn't charge the card.
Halfway down the article, he mentions PAYMENT_SERVER_URL which after all my digging I think might be the problem. I'm not 100% what he means by 'myapidomain'. When I'm not in production it's just my localhost:8080. I'm not sure what it would be on my production site.
I am hosting with HostGator if that helps. I've been trying to figure this out for about 3 days and am not sure where else to turn!
Frontend StripeComponent.js
import React, { Component } from 'react'
import StripeCheckout from 'react-stripe-checkout';
import axios from 'axios'
import STRIPE_PUB_KEY from '../../../constants/stripePubKey'
import PAYMENT_SERVER_URL from '../../../constants/stripeServer'
import { clearReduxCart } from '../../../ducks/reducer'
const CURRENCY = 'usd';
const successPayment = (redirectHome, clearReduxCart) => {
alert('Payment Successful');
localStorage.clear()
clearReduxCart()
return redirectHome(true)
};
const errorPayment = data => {
alert('Payment Error', data);
};
const onToken = (amount, description, email, redirectHome, clearReduxCart) => token =>
axios.post(PAYMENT_SERVER_URL,
{
description,
source: token.id,
currency: CURRENCY,
amount: amount,
receipt_email: token.email
})
.then(successPayment(redirectHome, clearReduxCart))
.catch(errorPayment);
const Checkout = ({ name, description, amount, email, redirectHome, clearReduxCart }) =>
<StripeCheckout
name={name}
description={description}
amount={amount}
email={email}
token={onToken(amount, description, email, redirectHome, clearReduxCart)}
currency={CURRENCY}
stripeKey={STRIPE_PUB_KEY}
shippingAddress
receipt_email
/>
export default Checkout
server/index.js
require('dotenv').config();
const express = require('express')
, SERVER_CONFIGS = require('./const/server')
, configureServer = require('./server')
, configureRoutes = require('./routes')
const app = express();
app.use( express.static( `${__dirname}/../build` ) );
configureServer(app);
configureRoutes(app);
app.listen(SERVER_CONFIGS.PORT, error => {
if (error) throw error;
console.log(`Server running on port ${ SERVER_CONFIGS.PORT }` );
});
server/server.js
const cors = require('cors');
const bodyParser = require('body-parser');
const CORS_WHITELIST = require('./const/frontend');
const corsOptions = {
origin: (origin, callback) =>
(CORS_WHITELIST.indexOf(origin) !== -1)
? callback(null, true)
: callback(new Error('Not allowed by CORS'))
};
const configureServer = app => {
app.use(cors());
app.use(bodyParser.json());
};
module.exports = configureServer;
server/routes/payments.js
const stripe = require('../const/stripe');
const postStripeCharge = res => (stripeErr, stripeRes) => {
if (stripeErr) {
res.status(500).send({ error: stripeErr });
} else {
res.status(200).send({ success: stripeRes });
}
}
const paymentApi = app => {
app.get('/', (req, res) => {
res.send({ message: 'Hello Stripe checkout server!', timestamp: new Date().toISOString() })
});
app.post('/', (req, res) => {
stripe.charges.create({
amount: req.body.amount,
currency: req.body.currency,
source: req.body.source,
description: req.body.description
}, postStripeCharge(res));
});
return app;
};
module.exports = paymentApi;

Resources