i am trying to delete a post by its id from the database. I was able to implement the get and post methods in React Native but when i tried to delete it did not work.
here is my delete method in backend:-
router.delete("/:id", (req, res) => {
let { id } = req.params;
Post.findByPk(id).then((post) => {
if (post) {
return post.destroy().then(() => {
res.status(204).send();
});
} else {
res.status(404).send();
}
});
});
in React Native here is the code i am currently using:
const deletePost = () => client.delete("/posts/:id");
here is my button, that when pressed calls the handleDelete function:
<Button title="Delete" onPress={handleDelete}/>
And here is my handle delete so far:
const handleDelete = () => {
Alert.alert("Delete", "Are you sure you want to delete this post?", [
{ text: "Yes", onPress=},
{ text: "No" },
]);
};
Can someone help me finish this? How do i call the deletePost method in my onPress?
Here is my Post Model in backend:
const Post = db.define(
"Post",
{
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
title: {
type: Sequelize.STRING,
})
You are not sending id from client. You're passing a string ":id" in that route. Try below code.
const deletePost = (id) => client.delete(`/posts/${id}`);
const handleDelete = (id) => {
Alert.alert("Delete", "Are you sure you want to delete this post?", [
{ text: "Yes", onPress: () => deletePost(id) },
{ text: "No" },
]);
};
Try this way
const handleDelete = () => {
Alert.alert("Delete", "Are you sure you want to delete this post?", [
{ text: "Yes", onPress: () => deletePost() },
{ text: "No" },
]);
};
Related
I am fairly new to using graphql-ws and graphql-yoga server, so forgive me if this is a naive question or mistake from my side.
I went through graphql-ws documentation. It has written the schema as a parameter. Unfortunately, the schema definition used in the documentation is missing a reference.
After adding a new todo (using addTodo) it shows two todo items. So I believe it is unable to return the initial todo list whenever running subscribe on Yoga Graphiql explorer.
It should show the initial todo item as soon as it has been subscribed and published in the schema definition.
My understanding is there is something I am missing in the schema definition which is not showing the todo list when tried accessing Yoga Graphiql explorer.
Has anyone had a similar experience and been able to resolve it? What I am missing?
Libraries used
Backend
graphql-yoga
ws
graphql-ws
Frontend
solid-js
wonka
Todo item - declared in schema
{
id: "1",
title: "Learn GraphQL + Solidjs",
completed: false
}
Screenshot
Code Snippets
Schema definition
import { createPubSub } from 'graphql-yoga';
import { Todo } from "./types";
let todos = [
{
id: "1",
title: "Learn GraphQL + Solidjs",
completed: false
}
];
// channel
const TODOS_CHANNEL = "TODOS_CHANNEL";
// pubsub
const pubSub = createPubSub();
const publishToChannel = (data: any) => pubSub.publish(TODOS_CHANNEL, data);
// Type def
const typeDefs = [`
type Todo {
id: ID!
title: String!
completed: Boolean!
}
type Query {
getTodos: [Todo]!
}
type Mutation {
addTodo(title: String!): Todo!
}
type Subscription {
todos: [Todo!]
}
`];
// Resolvers
const resolvers = {
Query: {
getTodos: () => todos
},
Mutation: {
addTodo: (_: unknown, { title }: Todo) => {
const newTodo = {
id: "" + (todos.length + 1),
title,
completed: false
};
todos.push(newTodo);
publishToChannel({ todos });
return newTodo;
},
Subscription: {
todos: {
subscribe: () => {
const res = pubSub.subscribe(TODOS_CHANNEL);
publishToChannel({ todos });
return res;
}
},
},
};
export const schema = {
resolvers,
typeDefs
};
Server backend
import { createServer } from "graphql-yoga";
import { WebSocketServer } from "ws";
import { useServer } from "graphql-ws/lib/use/ws";
import { schema } from "./src/schema";
import { execute, ExecutionArgs, subscribe } from "graphql";
async function main() {
const yogaApp = createServer({
schema,
graphiql: {
subscriptionsProtocol: 'WS', // use WebSockets instead of SSE
},
});
const server = await yogaApp.start();
const wsServer = new WebSocketServer({
server,
path: yogaApp.getAddressInfo().endpoint
});
type EnvelopedExecutionArgs = ExecutionArgs & {
rootValue: {
execute: typeof execute;
subscribe: typeof subscribe;
};
};
useServer(
{
execute: (args: any) => (args as EnvelopedExecutionArgs).rootValue.execute(args),
subscribe: (args: any) => (args as EnvelopedExecutionArgs).rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx);
const args: EnvelopedExecutionArgs = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe,
},
};
const errors = validate(args.schema, args.document);
if (errors.length) return errors;
return args;
},
},
wsServer,
);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
apply these changes
Mutation: {
addTodo: (_: unknown, { title }: Todo) => {
const newTodo = {
id: "" + (todos.length + 1),
title,
completed: false
};
todos.push(newTodo);
publishToChannel({ todos });
return newTodo;
},
Subscription: {
todos: {
subscribe: () => {
return Repeater.merge(
[
new Repeater(async (push, stop) => {
push({ todos });
await stop;
}),
pubSub.subscribe(TODOS_CHANNEL),
]
)
}
},
},
first, npm i #repeaterjs/repeater then import Repeater
I am having a problem that when user upload their profile image it did not change, user have to log out and log back in to make a change complete.
Here is my back end how to get image from client and store it on cloudinary:
profilesController.js:
exports.updateAvatar = async (req, res) => {
// Find user with matching token
// const updates = [];
const updateUserAvatar = await models.User.findOne({
where: {
id: req.id,
},
});
// Was user found?
if (updateUserAvatar === null) {
return res.status(200).json({
validationErrors: {
errors: [
{
msg: "Reset is invalid or has expired.",
},
],
},
});
}
// Update user with new info
models.User.update(
{
picture: req.imageUrl,
},
{
where: {
id: updateUserAvatar.dataValues.id,
},
}
);
console.log(updateUserAvatar);
At the console it should gave me a new image url but instead it just keep the old image url
Here is my profilesAPI where my route is:
router.post('/upload/image', function (req, res, next) {
const dUri = new Datauri();
const dataUri = (req) => dUri.format(path.extname(req.name).toString(), req.data);
if (req.files !== undefined && req.files !== null) {
const { file, id } = req.files;
const newFile = dataUri(file).content;
cloudinary.uploader.upload(newFile)
.then(result => {
const imageUrl = result.url;
const data = {id : req.body.id, imageUrl };
updateAvatar(data);
return res.status(200).json({ message: 'Success', data: { imageUrl } });
}).catch(err => res.status(400).json({message:'Error', data: { err}}));
} else {
return res.status(400).json({ message: 'Error' });
}
});
And that's all for my back end code. Here is my front end that cient send image to server:
Here is the method that help user can send image to server:
const UserCard = ({ name, userEmail, isVerified, id, updateUserAvatar, currentUser }) => {
const [selectedValue, setSelectedValue] = useState("a");
const handleChange = (event) => {
setSelectedValue(event.target.value);
};
const [imageSelected, setImageSelected] = useState("");
const uploadImage = () => {
const formData = new FormData();
formData.append("file", imageSelected);
formData.append("id", id);
axios
.post("/api/v1/profiles/upload/image", formData, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((response) => {
updateUserAvatar(response.data.data.imageUrl);
});
};
useEffect(() => {
if (imageSelected !== '') {
uploadImage();
}
}, [imageSelected]);
return (
<div className="avatar--icon_profile">
<Card className="profile--card_container">
<CardContent>
{currentUser.picture ? (
<div>
<input
className="my_file"
type="file"
ref={inputFile}
onChange={(e) => setImageSelected(e.target.files[0])}
/>
<div className="profile-image">
<Avatar
src={currentUser.picture}
alt="Avatar"
className="avatar--profile_image"
onClick={onButtonClick}
/>
</div>
</div>
and here is my Global State. I tried to update nested state in my context but seems like it didn't work.
const GlobalState = (props) => {
// User State -----------------------------------------------------------------------------
const [currentUser, setUser] = useState(props.serverUserData);
console.log(currentUser)
const updateUser = (userData) => {
setUser(userData);
};
// This method is passed through context to update currentUser Avatar
const updateUserAvatar = (picture) => {
setUser({ ...currentUser, picture: picture });
};
const providerValues = {
currentUser,
updateUser,
updateUserAvatar,
};
return (
<GlobalContext.Provider value={providerValues}>
{props.children}
</GlobalContext.Provider>
);
};
export default GlobalState;
and here is my console.log(currentUser) gave me:
{id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed", name: "Nhan Nguyen", email: "nhan13574#gmail.com", publicId: "Nh1615314435848", picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png", …}
email: "nhan13574#gmail.com"
id: "a19cac5c-ea25-4c9c-b1d9-5d6e464869ed"
isSessionValid: true
name: "Nhan Nguyen"
picture: "http://res.cloudinary.com/teammateme/image/upload/v1617229506/gnlooupiekujkrreerxn.png"
publicId: "Nh1615314435848"
__proto__: Object
Can anyone help me solve this problem? I really appreciate it
Added GlobalContext.js:
import React from "react";
const globalStateDefaults = {
modals: {
isAuthModalOpen: false,
modalToDisplay: "signup",
toggleModal: () => {},
setModalToDisplay: () => { },
},
user: undefined,
pageName: undefined,
loading: false,
teamProfileId: "",
userProfileId: "",
};
export const GlobalContext = React.createContext(globalStateDefaults);
You need to consume the context where you are trying to update user state.
const {currentUser, updateUser, updateUserAvatar} = React.useContext(GlobalContext)
Then you can call
updateUserAvatar(response.data.data.imageUrl)
i want collect news from different channels and echo them in a second channel, with the code i can read channels(not all but most).
I stuck now on the echo problem and have no clue about how i can do this, mtproto is completely new to me, thanks.
Im using the following code i have from another stackoverflow question.
const { MTProto, getSRPParams } = require('#mtproto/core');
const prompts = require('prompts');
const api_id = 'xxxxx';
const api_hash = 'xxxxx';
async function getPhone() {
return (await prompts({
type: 'text',
name: 'phone',
message: 'Enter your phone number:'
})).phone
}
async function getCode() {
// you can implement your code fetching strategy here
return (await prompts({
type: 'text',
name: 'code',
message: 'Enter the code sent:',
})).code
}
async function getPassword() {
return (await prompts({
type: 'text',
name: 'password',
message: 'Enter Password:',
})).password
}
const mtproto = new MTProto({
api_id,
api_hash,
});
function startListener() {
console.log('[+] starting listener')
mtproto.updates.on('updates', ({ updates }) => {
const newChannelMessages = updates.filter((update) => update._ === 'updateNewChannelMessage').map(({ message }) => message) // filter `updateNewChannelMessage` types only and extract the 'message' object
for (const message of newChannelMessages) {
// printing new channel messages
console.log(`[${message.to_id.channel_id}] ${message.message}`)
}
});
}
// checking authentication status
mtproto
.call('users.getFullUser', {
id: {
_: 'inputUserSelf',
},
})
.then(startListener) // means the user is logged in -> so start the listener
.catch(async error => {
// The user is not logged in
console.log('[+] You must log in')
const phone_number = await getPhone()
mtproto.call('auth.sendCode', {
phone_number: phone_number,
settings: {
_: 'codeSettings',
},
})
.catch(error => {
if (error.error_message.includes('_MIGRATE_')) {
const [type, nextDcId] = error.error_message.split('_MIGRATE_');
mtproto.setDefaultDc(+nextDcId);
return sendCode(phone_number);
}
})
.then(async result => {
return mtproto.call('auth.signIn', {
phone_code: await getCode(),
phone_number: phone_number,
phone_code_hash: result.phone_code_hash,
});
})
.catch(error => {
if (error.error_message === 'SESSION_PASSWORD_NEEDED') {
return mtproto.call('account.getPassword').then(async result => {
const { srp_id, current_algo, srp_B } = result;
const { salt1, salt2, g, p } = current_algo;
const { A, M1 } = await getSRPParams({
g,
p,
salt1,
salt2,
gB: srp_B,
password: await getPassword(),
});
return mtproto.call('auth.checkPassword', {
password: {
_: 'inputCheckPasswordSRP',
srp_id,
A,
M1,
},
});
});
}
})
.then(result => {
console.log('[+] successfully authenticated');
// start listener since the user has logged in now
startListener()
});
})
I am getting error 404 that the API cannot be found. The Boolean in API is "auto_delete". I want to change it to true when this is referenced.
I wrote a button to reference a function that would reference this API but OnClick it will not be able to find this API apparently.
I posted first the front end file with constructor/binding, button, and function. I then put API at bottom.
Any input would help.
//FRONT END
This is the front end with button and function button references to
//CONSTRUCTOR AND BINDING FUNCTION
export default class VideoContent extends Component {
constructor(props) {
super(props);
let video = this.props.video;
let user = this.props.user;
this.state = {
fetchDuration: false,
viewed: false,
subscribed:
user &&
user.hasOwnProperty("follows") &&
user.follows.includes(video.author)
};
this.view = this.view.bind(this);
this.report = this.report.bind(this);
this.reportJ= this.reportJ.bind(this);
}
<Button
disabled={user ? !user.userId : true}
onClick={this.reportJ}
style={{ float: "right", backgroundColor: "purple" }}
>
ReportJ
</Button>
//FUNCTION
reportJ() {
const { video, user } = this.props;
confirmAlert({
title: "Confirm Objectionable Content",
message: "You Sure Fool?",
buttons: [
{
label: "Yes",
onClick: () => {
axios
.post(
inDev
? devAPI.concat("reportJ")
: baseAPI.concat("reportJ"),
{
id: video._id,
update: {
auto_delete: true
}
}
)
.then(res => {
this.setState({ changed: true });
})
.catch(error => {
console.log(error);
});
}
},
{
label: "No",
onClick: () => alert("Nevermind")
}
]
});
}
//SERVER JS File with API below
router.post("/reportJ", (req, res) => {
const { id, update } = req.body;
models.Videos.findById(id, function (err, video) {
video.auto_delete = true;
video.save((err, data) => {
if (err)
return res.json({
success: false,
error: err
});
return res.json({
success: true,
data: data
});
});
});
});
I am using mocha and chai for writing test for RESTful APIs
I have read some articles where people suggests to create stubs for queries, and you shouldn't be actually making a database query.
But How would I make sure if it works?
See below controller.
const Op = require('sequelize').Op
//Models
const {
Item,
Location,
Combo,
Service,
ComboItem,
ItemLocation
} = require('../models')
const _ = require('lodash')
//Services
const paginate = require('../services/PaginationService')
const getAllItems = async function(req, res) {
if(req.query.location_id){
let items
const item = await Location.findOne({
where: {
id: 1
},
include: {
model: Item,
through: {
model: ItemLocation,
attributes: []
},
as: 'itemsAtLocation',
include: [
{
model: Service,
as: 'service',
attributes: ["id"]
},
{
model: Combo,
as: 'combo',
attributes: ["start_date", "expiry_date"]
}
]
}
})
if(!item)
return res.status(200).send({
status: true,
message: "No item found at location!",
data: {}
})
items = item.itemsAtLocation
let data = {}
data.services = []
data.combos = []
_.forEach(items, item => {
let itemData = {
id: item.id,
name: item.name,
price: item.price,
discount_per: item.discount_per,
}
if(item.service)
data.services.push(itemData)
if(item.combo) {
itemData.start_date = item.combo.start_date
itemData.expiry_date = item.combo.expiry_date
data.combos.push(itemData)
}
})
return res.status(200).send({
status: true,
message: "Successfully fetch all items!",
data: data
})
} else {
const items = await Item.findAll({
include: [
{
model: Service,
as: 'service',
attributes: ["id"]
},
{
model: Combo,
as: 'combo',
attributes: ["start_date", "expiry_date"]
}
],
attributes: ["id", "name", "price", "discount_per", "description"],
...paginate(+req.query.page, +req.query.per_page)
})
let data = {}
data.services = []
data.combos = []
_.forEach(items, item => {
let itemData = {
id: item.id,
name: item.name,
price: item.price,
discount_per: item.discount_per,
}
if(item.service)
data.services.push(itemData)
if(item.combo) {
itemData.start_date = item.combo.start_date
itemData.expiry_date = item.combo.expiry_date
data.combos.push(itemData)
}
})
return res.status(200).send({
status: true,
message: "Successfully fetch all items!",
data: data
})
}
}
module.exports = {
getAllItems
}
As you can see from above code. I need queries to return data in a specific form. If it won't be in that form things won't work.
Can someone suggest how can I create stubs for such kind of functions so that structure also be preserved?
Below is the test that I have wrote, But it uses actual db calls.
describe('GET /api/v1/items', function () {
it('should fetch all items orgianized by their type', async () => {
const result = await request(app)
.get('/api/v1/items')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
expect(result)
.to.be.a('Object')
expect(result.body.status)
.to.be.a('Boolean').true
expect(result.body.data, "data should be an Object and every key should an Array")
.to.satisfy(data => {
expect(data).to.be.a('Object')
.to.not.be.null
if(!_.isEmpty(data)) {
expect(data).to.have.any.keys('services', 'combos')
_.forOwn(data, (value, key) => {
expect(data[key]).to.be.a('Array')
})
return true
}
return true
})
})
})
One way you can do that is by stubbing the methods from your models, i.e. Location.findOne and Item.findAll. So your tests could look a bit like the code below:
const sinon = require('sinon');
const Location = require('../models/location'); // Get your location model
const Item = require('../models/item'); // Get your item model
describe('myTest', () => {
let findOneLocationStub;
let findAllItemsStub;
beforeEach(() => {
findOneLocationStub = sinon.stub(Location, 'findOne');
findAllItemsStub = sinon.stub(Item, 'findAll');
});
afterEach(() => {
findOneLocationStub.verifyAndRestore();
findAllItemsStub.verifyAndRestore();
});
it('returns 200 when location not found', () => {
findOneLocationStub.resolves(null);
expects...
});
});
I did not run the test, but something like that should work. But note that I had to split the models into their own file to do the stub. Probably there's a way to do the same using your current implementation.
Another thing I would suggest is having some kind of use case into your method that is responsible for database implementation. Something like:
const getAllItemsUseCase = (params, queryService) => {
if(params.locationId){
let items
const item = await queryService.findOneLocation({
};
So when you call this method from your controller, you can do call:
const getAllItems = async function(req, res) {
const params = {
locationId: req.query.location_id,
// and more parameters
};
const queryService = {
findOneLocation: Location.findOne,
};
const results = await getAllItemsUseCase(params, queryService);
}
This way you will detach your business logic from the controller and you will have a much easier time to mock your query: you just change the methods provided to queryService.
You can find some interesting read from this blog post: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html