slates: supports deleting slates

This commit is contained in:
@wwwjim 2020-08-06 19:17:49 -07:00
parent a23b8b9926
commit e79f611fda
7 changed files with 204 additions and 114 deletions

View File

@ -8,6 +8,19 @@ const REQUEST_HEADERS = {
"Content-Type": "application/json",
};
const DEFAULT_OPTIONS = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
};
const returnJSON = async (route, options) => {
const response = await fetch(route, options);
const json = await response.json();
return json;
};
export const sendFilecoin = async (data) => {
if (Strings.isEmpty(data.source)) {
return null;
@ -21,158 +34,96 @@ export const sendFilecoin = async (data) => {
return null;
}
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/addresses/send`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/addresses/send`, options);
const json = await response.json();
return json;
});
};
export const updateViewer = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/users/update`, {
...DEFAULT_OPTIONS,
body: JSON.stringify(data),
};
const response = await fetch(`/api/users/update`, options);
const json = await response.json();
return json;
});
};
export const signIn = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/sign-in`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/sign-in`, options);
const json = await response.json();
return json;
});
};
export const hydrateAuthenticatedUser = async () => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
};
const response = await fetch(`/api/hydrate`, options);
const json = await response.json();
return json;
return await returnJSON(`/api/hydrate`, {
...DEFAULT_OPTIONS,
});
};
export const deleteViewer = async () => {
const options = {
method: "DELETE",
headers: REQUEST_HEADERS,
credentials: "include",
};
const response = await fetch(`/api/users/delete`, options);
const json = await response.json();
return json;
return await returnJSON(`/api/users/delete`, {
...DEFAULT_OPTIONS,
});
};
export const createUser = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/users/create`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/users/create`, options);
const json = await response.json();
return json;
});
};
export const checkCIDStatus = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/data/cid-status`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/data/cid-status`, options);
const json = await response.json();
return json;
});
};
export const health = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/_`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data: { success: true } }),
};
const response = await fetch(`/api/_`, options);
const json = await response.json();
return json;
});
};
export const createSlate = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/slates/create`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/slates/create`, options);
const json = await response.json();
return json;
});
};
export const updateSlate = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/slates/update`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
});
};
const response = await fetch(`/api/slates/update`, options);
const json = await response.json();
return json;
export const deleteSlate = async (data) => {
return await returnJSON(`/api/slates/delete`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
});
};
export const deleteSlateItem = async (data) => {
return await returnJSON(`/api/slates/remove-item`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
});
};
export const generateAPIKey = async () => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
};
const response = await fetch(`/api/keys/generate`, options);
const json = await response.json();
return json;
return await returnJSON(`/api/keys/generate`, {
...DEFAULT_OPTIONS,
});
};
export const deleteAPIKey = async (data) => {
const options = {
method: "POST",
headers: REQUEST_HEADERS,
credentials: "include",
return await returnJSON(`/api/keys/delete`, {
...DEFAULT_OPTIONS,
body: JSON.stringify({ data }),
};
const response = await fetch(`/api/keys/delete`, options);
const json = await response.json();
return json;
});
};

View File

@ -104,7 +104,7 @@ const Item = ({
return (
<span
css={STYLES_NAVIGATION_ITEM}
style={{ padding: `0 0 0 ${level * 16}px` }}
style={{ padding: `0 24px 0 ${level * 16}px` }}
>
<span css={STYLES_EXPANDER} onClick={onToggleShow ? onToggleShow : null}>
<span

View File

@ -10,6 +10,7 @@ import getSlateById from "~/node_common/data/methods/get-slate-by-id";
import getSlatesByUserId from "~/node_common/data/methods/get-slates-by-user-id";
import updateSlateById from "~/node_common/data/methods/update-slate-by-id";
import deleteSlatesForUserId from "~/node_common/data/methods/delete-slates-for-user-id";
import deleteSlateById from "~/node_common/data/methods/delete-slate-by-id";
import createAPIKeyForUserId from "~/node_common/data/methods/create-api-key-for-user-id";
import deleteAPIKeyById from "~/node_common/data/methods/delete-api-key-by-id";
@ -32,6 +33,7 @@ export {
getSlatesByUserId,
updateSlateById,
deleteSlatesForUserId,
deleteSlateById,
// NOTE(jim): API key operations,
createAPIKeyForUserId,
deleteAPIKeyById,

View File

@ -0,0 +1,20 @@
import { runQuery } from "~/node_common/data/utilities";
export default async ({ id }) => {
return await runQuery({
label: "DELETE_SLATE_BY_ID",
queryFn: async (DB) => {
const data = await DB.from("slates")
.where({ id })
.del();
return 1 === data;
},
errorFn: async (e) => {
return {
error: "DELETE_SLATE_BY_ID",
source: e,
};
},
});
};

View File

@ -4,8 +4,12 @@ export default async ({ userId }) => {
return await runQuery({
label: "DELETE_SLATES_FOR_USER_ID",
queryFn: async (DB) => {
const hasUser = (id) => DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ ownerId: id })]);
const data = await DB.select("*").from("slates").where(hasUser(userId)).del();
const hasUser = (id) =>
DB.raw(`?? @> ?::jsonb`, ["data", JSON.stringify({ ownerId: id })]);
const data = await DB.select("*")
.from("slates")
.where(hasUser(userId))
.del();
return 1 === data;
},

View File

@ -0,0 +1,69 @@
import * as MW from "~/node_common/middleware";
import * as Utilities from "~/node_common/utilities";
import * as Data from "~/node_common/data";
import * as Strings from "~/common/strings";
const initCORS = MW.init(MW.CORS);
const initAuth = MW.init(MW.RequireCookieAuthentication);
export default async (req, res) => {
initCORS(req, res);
initAuth(req, res);
const id = Utilities.getIdFromCookie(req);
if (!id) {
return res
.status(500)
.json({ decorator: "SERVER_DELETE_SLATE", error: true });
}
const user = await Data.getUserById({
id,
});
if (!user) {
return res.status(404).json({
decorator: "SERVER_DELETE_SLATE_USER_NOT_FOUND",
error: true,
});
}
if (user.error) {
return res.status(500).json({
decorator: "SERVER_DELETE_SLATE_USER_NOT_FOUND",
error: true,
});
}
const slate = await Data.getSlateById({ id: req.body.data.id });
if (!slate) {
return res
.status(404)
.json({ decorator: "SERVER_DELETE_SLATE_SLATE_NOT_FOUND", error: true });
}
if (slate.error) {
return res
.status(500)
.json({ decorator: "SERVER_DELETE_SLATE_SLATE_NOT_FOUND", error: true });
}
const deleteResponse = await Data.deleteSlateById({ id: slate.id });
if (!deleteResponse) {
return res
.status(404)
.json({ decorator: "SERVER_DELETE_SLATE", error: true });
}
if (deleteResponse.error) {
return res
.status(500)
.json({ decorator: "SERVER_DELETE_SLATE", error: true });
}
return res
.status(200)
.json({ decorator: "SERVER_DELETE_SLATE", error: false });
};

View File

@ -72,6 +72,35 @@ export default class SceneSlate extends React.Component {
this.setState({ loading: false });
};
_handleDelete = async (e) => {
this.setState({ loading: true });
if (
!window.confirm(
"Are you sure you want to delete this Slate? This action is irreversible."
)
) {
return this.setState({ loading: false });
}
const response = await Actions.deleteSlate({
id: this.props.current.slateId,
});
if (!response) {
alert("TODO: Server Error");
return this.setState({ loading: false });
}
if (response.error) {
alert(`TODO: ${response.decorator}`);
return this.setState({ loading: false });
}
await this.props.onAction({ type: "NAVIGATE", value: 3, data: {} });
return await this.props.onRehydrate();
};
_handleChange = (e) => {
if (e && e.persist) {
e.persist();
@ -176,6 +205,21 @@ export default class SceneSlate extends React.Component {
Save changes
</System.ButtonPrimary>
</div>
<System.DescriptionGroup
style={{ marginTop: 48 }}
label="Delete this slate"
description="This action is irreversible."
/>
<div style={{ marginTop: 32 }}>
<System.ButtonSecondary
onClick={this._handleDelete}
loading={this.state.loading}
>
Delete {this.state.slatename}
</System.ButtonSecondary>
</div>
</ScenePage>
);
}