extract some queries from the app pages

This commit is contained in:
Shoofle 2024-09-19 15:43:50 -04:00
parent d5ef81007d
commit 6ce4def6e5
8 changed files with 171 additions and 114 deletions

View File

@ -4,85 +4,95 @@ import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react'; import { useState } from 'react';
import { apiUrl, fetchPage, postPage, deletePage } from './clientStuff.jsx';
const url = "http://127.0.0.1:3001"
function PageView() { function PageView() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate(); const navigate = useNavigate();
const { pagenumber } = useParams(); const { pagenumber } = useParams();
const [ text_of_page, setBodyText ] = useState("Not loaded yet");
const { isPending, error, data } = useQuery({ // fetch the currrent values const fetchQuery = useQuery({ // fetch the currrent values
queryKey: ['page', pagenumber], queryKey: ['page', pagenumber],
queryFn: () => queryFn: () => fetchPage(pagenumber)
fetch(`${url}/page/${pagenumber}`, {
method: 'GET',
headers: {'Content-Type': 'application/json'}
}).then((res) => res.json())
.then((data) => {
setBodyText(data[2])
return data;
})
}) })
const mutation = useMutation({ // for changing the value when we're done with it const postMutation = useMutation({ // for changing the value when we're done with it
mutationFn: ({id, title, description}) => { mutationFn: ({id, title, description}) => postPage({id, title, description}),
console.log("trying to update!");
const body = JSON.stringify({id: id, title: title, description: description})
console.log(`sending ${body}`)
fetch(`${url}/page/${pagenumber}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: body
}).then((res) => { console.log("tried to update!", res.json()) })
.catch((error) => { console.log("got an error", error.json()) });
},
onSettled: async (data, error, variables) => { onSettled: async (data, error, variables) => {
// Invalidate and refetch // Invalidate and refetch
await queryClient.invalidateQueries({ queryKey: ['page', variables.id] }) await queryClient.invalidateQueries({ queryKey: ['page', variables.id] })
}, },
}); });
function handleSubmit(e) { const deleteMutation = useMutation({ // for changing the value when we're done with it
e.preventDefault() mutationFn: (id) => deletePage(id),
const data = e.target.querySelector('pre').innerHTML onSettled: async (data, error, variables) => {
const formData = new FormData(e.target); // Invalidate and refetch
mutation.mutate({ await queryClient.invalidateQueries({ queryKey: ['pages'] })
},
});
const ready = !(fetchQuery.error || fetchQuery.isPending);
let the_id, page_title, page_text, page_html;
if (ready) [the_id, page_title, page_text, page_html] = fetchQuery.data;
if (!page_title) page_title = " ";
if (!page_text) page_text = " ";
function submitChanges(e) {
const newTitle = document.querySelector('span').innerHTML;
const newText = document.querySelector('pre').innerHTML;
postMutation.mutate({
id: pagenumber, id: pagenumber,
title: page_title, title: newTitle,
description: data, description: newText,
}); });
navigate(`/${pagenumber}`) navigate(`/${pagenumber}`)
} }
if (isPending) return "Loading..."; function submitDelete(e) {
e.preventDefault();
deleteMutation.mutate(pagenumber);
navigate(`/`);
}
if (error) return "Uh oh!";
const [the_id, page_title, page_text] = data;
return ( return (
<div className="App"> <div className="App">
<form onSubmit={handleSubmit}>
<header className="App-header"> <header className="App-header">
<img src={logo} className="App-logo" alt="logo" /> <a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<div className="Page-title"> <div className="Page-title">
<h3>{the_id}. {page_title}</h3> <h3>
{pagenumber}.
<span
contentEditable="true"
dangerouslySetInnerHTML={{__html: ready ? page_title : "..." }} />
</h3>
</div> </div>
<div className="Page-contents"> { ready ?
<pre <>
contentEditable="true" <div className="Page-contents">
onChange={(e) => setBodyText(e.target.value)} <pre
dangerouslySetInnerHTML={{__html: text_of_page}} /> contentEditable="true"
</div> dangerouslySetInnerHTML={{__html: page_text}} />
<button </div>
type="submit" <button
disabled={mutation.isPending}> disabled={postMutation.isPending}
{mutation.isPending ? "Updating..." : "Update"} onClick={submitChanges}>
</button> {postMutation.isPending ? "Updating..." : "Update"}
</button>
<button
disabled={deleteMutation.isPending}
onClick={submitDelete}>
{deleteMutation.isPending ? "Deleting..." : "Delete this page (no backsies)"}
</button>
</>
:
<div className="Page-contents">
{ fetchQuery.isPending ? "Loading..." : JSON.stringify(fetchQuery.error) }
</div>
}
</header> </header>
</form>
</div> </div>
); );
} }

View File

@ -4,32 +4,22 @@ import './App.css';
import { useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
const url = "http://127.0.0.1:3001" import { apiUrl, fetchPageList, postNewPage } from './clientStuff.jsx';
function Landing() { function Landing() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate(); const navigate = useNavigate();
const { isPending, error, data } = useQuery({ // fetch the currrent values const { isPending, error, data } = useQuery({ // fetch the currrent values
queryKey: ['pages'], queryKey: ['pages'],
queryFn: () => queryFn: fetchPageList
fetch(`${url}/pages`, {
method: 'GET',
headers: {'Content-Type': 'application/json'}
}).then((res) => res.json())
}) })
const makeNewPage = useMutation({ // for changing the value when we're done with it const makeNewPage = useMutation({ // for changing the value when we're done with it
mutationFn: () => mutationFn: () => postNewPage()
fetch(`${url}/page/new`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
}).then((res) => res.json())
.then((data) => data.id)
.catch((error) => { console.log("got an error", error.json()) }), .catch((error) => { console.log("got an error", error.json()) }),
onSettled: async (data, error, variables) => { onSettled: async (data, error, variables) => {
// Invalidate and navigate to the new page // Invalidate and navigate to the new page
await queryClient.invalidateQueries({ queryKey: ['pages'] }); await queryClient.invalidateQueries({ queryKey: ['pages'] });
console.log(data);
navigate(`/${data}/edit`); navigate(`/${data}/edit`);
}, },
}); });
@ -37,13 +27,15 @@ function Landing() {
return ( return (
<div className="App"> <div className="App">
<header className="App-header"> <header className="App-header">
<img src={logo} className="App-logo" alt="logo" /> <a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<h3>Welcome to the forest!</h3> <h3>Welcome to the forest!</h3>
<h4>This is some random stuff I added!</h4>
{ isPending ? { isPending ?
"Loading..." : "Loading..." :
(error ? (error ?
(<pre>{error}</pre>) : <pre>{error}</pre>
<ol> :
<ul>
{ {
data.map((row) => data.map((row) =>
<li> <li>
@ -51,10 +43,10 @@ function Landing() {
</li> </li>
) )
} }
</ol> </ul>
) )
} }
<button onClick={makeNewPage.mutate} >Make new page!</button> <button onClick={makeNewPage.mutate} >Dig!</button>
</header> </header>
</div> </div>
); );

View File

@ -4,46 +4,41 @@ import './App.css';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import { useState } from 'react'; import { useState } from 'react';
import { apiUrl, fetchPage, postPage } from './clientStuff.jsx';
const url = "http://localhost:3001"
function PageView() { function PageView() {
const navigate = useNavigate();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const navigate = useNavigate();
const { pagenumber } = useParams(); const { pagenumber } = useParams();
const [ text_of_page, setBodyText ] = useState("Not loaded yet");
const { isPending, error, data } = useQuery({ // fetch the currrent values const { isPending, error, data } = useQuery({ // fetch the currrent values
queryKey: ['page', pagenumber], queryKey: ['page', pagenumber],
queryFn: () => queryFn: () => fetchPage(pagenumber)
fetch(`${url}/page/${pagenumber}`, {
method: 'GET',
headers: {'Content-Type': 'application/json'}
}).then((res) => res.json())
.then((data) => {
//console.log(`got ${data} as a response for ${pagenumber}`);
setBodyText(data[3]);
return data;
})
}) })
if (isPending) return "Loading..."; const ready = !(error || isPending);
if (error) return "Uh oh!"; let the_id, page_title, page_text, page_html;
if (data) [the_id, page_title, page_text, page_html] = data;
const [the_id, page_title, page_text, page_html] = data;
return ( return (
<div className="App"> <div className="App">
<header className="App-header"> <header className="App-header">
<img src={logo} className="App-logo" alt="logo" /> <a href="/"><img src={logo} className="App-logo" alt="logo" /></a>
<div className="Page-title"> <div className="Page-title">
<h3>{the_id}. {page_title}</h3> <h3>{pagenumber}. {ready ? (page_title || " ") : "..."}</h3>
</div> </div>
<div { ready ?
className="Page-contents" <div
dangerouslySetInnerHTML={{ __html: text_of_page }} className="Page-contents"
/> dangerouslySetInnerHTML={{ __html: (page_html || " ") }}
/>
:
<div className="Page-contents">
{ isPending ? "Loading..." : JSON.stringify(error) }
</div>
}
<button type="submit" onClick={() => navigate(`/${pagenumber}/edit`)}>Edit this page!</button> <button type="submit" onClick={() => navigate(`/${pagenumber}/edit`)}>Edit this page!</button>
</header> </header>
</div> </div>

View File

@ -0,0 +1,43 @@
export const apiUrl = "http://127.0.0.1:3001"
const defaults = {
headers: {'Content-Type': 'application/json'}
};
export async function postNewPage() {
return fetch(`${apiUrl}/page/new`, {
method: 'POST',
...defaults
}).then((res) => res.json())
.then((data) => data.id)
}
export async function fetchPageList() {
return fetch(`${apiUrl}/pages`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function fetchPage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'GET',
...defaults
}).then((res) => res.json())
}
export async function postPage({id, title, description}) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'POST',
body: JSON.stringify({id: id, title: title, description: description}),
...defaults
})
}
export async function deletePage(id) {
return fetch(`${apiUrl}/page/${id}`, {
method: 'DELETE',
headers: {'Content-Type': 'application/json'},
...defaults
})
}

View File

@ -1 +1 @@
{"id":"7de13931-444c-4dd2-a19a-5cd1e70b7f36","rows_written":87,"rows_read":1016,"storage_bytes_used":12288,"write_requests_delegated":0,"current_frame_no":86,"top_query_threshold":3,"top_queries":[{"rows_written":2,"rows_read":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n"},{"rows_written":0,"rows_read":4,"query":"select id from pages"},{"rows_written":0,"rows_read":5,"query":"select id from pages"},{"rows_written":1,"rows_read":4,"query":"\nalter table pages \nadd column html text\n"},{"rows_written":0,"rows_read":6,"query":"\nselect p.id from pages p where not exists (select r.id from renders r where p.id = r.id)\n"},{"rows_written":0,"rows_read":10,"query":"select id from pages"},{"rows_written":0,"rows_read":11,"query":"select id from pages"},{"rows_written":0,"rows_read":12,"query":"select id from pages"},{"rows_written":0,"rows_read":13,"query":"select id from pages"},{"rows_written":0,"rows_read":14,"query":"select id from pages"}],"slowest_query_threshold":0,"slowest_queries":[{"elapsed_ms":0,"query":"select id, title, description from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":2},{"elapsed_ms":1,"query":"\nalter table pages \nadd column html text\n","rows_written":1,"rows_read":4},{"elapsed_ms":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n","rows_written":2,"rows_read":1},{"elapsed_ms":1,"query":"insert into pages (title, description) values (?, ?) returning id","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"select * from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":1,"query":"select count(*) as num_pages from pages","rows_written":0,"rows_read":1},{"elapsed_ms":7,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1}],"embedded_replica_frames_replicated":0,"query_count":665,"query_latency":74555} {"id":"7de13931-444c-4dd2-a19a-5cd1e70b7f36","rows_written":109,"rows_read":1706,"storage_bytes_used":12288,"write_requests_delegated":0,"current_frame_no":108,"top_query_threshold":3,"top_queries":[{"rows_written":2,"rows_read":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n"},{"rows_written":0,"rows_read":4,"query":"select id from pages"},{"rows_written":0,"rows_read":5,"query":"select id from pages"},{"rows_written":1,"rows_read":4,"query":"\nalter table pages \nadd column html text\n"},{"rows_written":0,"rows_read":6,"query":"\nselect p.id from pages p where not exists (select r.id from renders r where p.id = r.id)\n"},{"rows_written":0,"rows_read":10,"query":"select id from pages"},{"rows_written":0,"rows_read":11,"query":"select id from pages"},{"rows_written":0,"rows_read":12,"query":"select id from pages"},{"rows_written":0,"rows_read":13,"query":"select id from pages"},{"rows_written":0,"rows_read":14,"query":"select id from pages"}],"slowest_query_threshold":0,"slowest_queries":[{"elapsed_ms":0,"query":"select id, title, description from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":1},{"elapsed_ms":0,"query":"select title from pages","rows_written":0,"rows_read":2},{"elapsed_ms":1,"query":"\nalter table pages \nadd column html text\n","rows_written":1,"rows_read":4},{"elapsed_ms":1,"query":"\ncreate table if not exists renders (\n id integer primary key,\n title varchar(255),\n description text\n)\n","rows_written":2,"rows_read":1},{"elapsed_ms":1,"query":"insert into pages (title, description) values (?, ?) returning id","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1},{"elapsed_ms":1,"query":"select * from pages where id=@val","rows_written":0,"rows_read":1},{"elapsed_ms":1,"query":"select count(*) as num_pages from pages","rows_written":0,"rows_read":1},{"elapsed_ms":7,"query":"replace into pages (id, title, description) values (?, ?, ?)","rows_written":1,"rows_read":1}],"embedded_replica_frames_replicated":0,"query_count":877,"query_latency":90259}

Binary file not shown.

View File

@ -35,26 +35,9 @@ app.get('/pages', (req, res) => {
.then((data) => data[0].results.rows) .then((data) => data[0].results.rows)
.then((rows) => rows.map(([id]) => id)) .then((rows) => rows.map(([id]) => id))
.then((ids) => res.json(ids)) .then((ids) => res.json(ids))
.catch((error) => res.status(404).json({"error": "error"})); .catch((error) => res.status(500).json({"error": error}));
}); });
app.get('/page/:id', (req, res) => {
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'select * from pages where id=?',
params: [req.params.id]
}]
})
}).then((rsp) => rsp.json())
.then((data) => data[0].results.rows[0])
.then((row) => res.json(row))
.catch((error) => res.status(404).json({"error": error}));
});
app.post('/page/new', (req, res) => { app.post('/page/new', (req, res) => {
fetch(db_url, { fetch(db_url, {
method: 'POST', method: 'POST',
@ -68,7 +51,25 @@ app.post('/page/new', (req, res) => {
}).then((rsp) => rsp.json()) }).then((rsp) => rsp.json())
.then((data) => data[0].results.rows[0][0]) .then((data) => data[0].results.rows[0][0])
.then((row) => res.status(200).json({id: row})) .then((row) => res.status(200).json({id: row}))
.catch((error) => res.status(404).json({"error": error})) .catch((error) => res.status(500).json({"error": error}))
});
app.get('/page/:id', (req, res) => {
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'select * from pages where id=?',
params: [req.params.id]
}]
})
}).then((rsp) => rsp.json())
.then((data) => {
if (data[0].results.rows.length == 0) return res.status(404).json({"error": "page not found in db"});
return data[0].results.rows[0];
}).then((row) => res.status(200).json(row))
.catch((error) => res.status(500).json({"error": error}));
}); });
app.post('/page/:id', (req, res) => { app.post('/page/:id', (req, res) => {
@ -90,5 +91,21 @@ app.post('/page/:id', (req, res) => {
}] }]
}) })
}).then(() => res.status(200).json({})) }).then(() => res.status(200).json({}))
.catch((error) => res.status(404).json({"error": error})); .catch((error) => res.status(500).json({"error": error}));
}); });
app.delete('/page/:id', (req, res) => {
console.log(`deleting page #${req.params.id}`);
fetch(db_url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
statements: [{
q: 'delete from pages where id = ?',
params: [Number(req.params.id)]
}]
})
}).then(() => res.status(200).json({id: req.params.id}))
.catch((error) => res.status(500).json({"error": error}))
})