Skip to content

Commit

Permalink
feat: reject saves when updatedAt is stale, closes #155
Browse files Browse the repository at this point in the history
  • Loading branch information
neopostmodern committed Aug 4, 2022
1 parent ef220fa commit b1cd50c
Show file tree
Hide file tree
Showing 13 changed files with 74 additions and 16 deletions.
1 change: 1 addition & 0 deletions client/src/renderer/components/LinkForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import MarkedTextarea from './MarkedTextarea';

const linkFormFields: Array<keyof LinkQuery_link> = [
'_id',
'updatedAt',
'url',
'name',
'description',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { MutationResult } from '@apollo/client';
import { Box, Typography } from '@mui/material';
import { PropsWithChildren, useEffect, useState } from 'react';
import { DataState, PolicedData } from '../utils/useDataState';
import ErrorSnackbar from './ErrorSnackbar';

enum NetworkPhase {
IDLE,
Expand Down Expand Up @@ -79,7 +80,12 @@ const NetworkOperationsIndicator = ({
message = 'Up to date.';
}

return <NetworkIndicatorContainer>{message}</NetworkIndicatorContainer>;
return (
<>
<NetworkIndicatorContainer>{message}</NetworkIndicatorContainer>
<ErrorSnackbar error={mutation?.error} actionDescription="save" />
</>
);
};

export default NetworkOperationsIndicator;
18 changes: 11 additions & 7 deletions client/src/renderer/components/TagForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { pick } from 'lodash';
import React from 'react';
import { useForm } from 'react-hook-form';
import styled from 'styled-components';
import { TagType } from '../types';
import { TagObject } from '../reducers/links';
import colorTools from '../utils/colorTools';
import useSaveOnUnmount from '../utils/useSaveOnUnmount';
import { TextField } from './CommonStyles';
Expand All @@ -16,19 +16,23 @@ const ColorBlockInput = styled(TextField)`
text-align: center;
`;

const tagFormFields = ['_id', 'color', 'name'];
const tagFormFields: Array<keyof TagObject> = [
'_id',
'updatedAt',
'color',
'name',
];
type TagInForm = Pick<TagObject, typeof tagFormFields[number]>;

interface TagFormProps {
tag: TagType;
onSubmit: (tag: TagType) => void;
tag: TagObject;
onSubmit: (tag: TagObject) => void;
}

const TagForm: React.FC<TagFormProps> = ({ tag, onSubmit }) => {
const defaultValues = pick(tag, tagFormFields);

const { register, getValues, handleSubmit } = useForm<
Pick<TagType, typeof tagFormFields[number]>
>({
const { register, getValues, handleSubmit } = useForm<TagInForm>({
defaultValues,
mode: 'onBlur',
resolver: (formValues) => {
Expand Down
7 changes: 6 additions & 1 deletion client/src/renderer/components/TextForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import { NameInput } from './formComponents';
import Gap from './Gap';
import MarkedTextarea from './MarkedTextarea';

const textFormFields: Array<keyof NoteObject> = ['_id', 'name', 'description'];
const textFormFields: Array<keyof NoteObject> = [
'_id',
'updatedAt',
'name',
'description',
];
type TextInForm = Pick<NoteObject, typeof textFormFields[number]>;

interface TextFormProps {
Expand Down
6 changes: 5 additions & 1 deletion client/src/renderer/containers/LinkPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const LINK_QUERY = gql`
link(linkId: $linkId) {
_id
createdAt
updatedAt
archivedAt
url
name
Expand All @@ -41,6 +42,7 @@ const UPDATE_LINK_MUTATION = gql`
updateLink(link: $link) {
_id
createdAt
updatedAt
url
domain
name
Expand Down Expand Up @@ -71,7 +73,9 @@ const LinkPage: React.FC = () => {
>(UPDATE_LINK_MUTATION);
const handleSubmit = useCallback(
(updatedLink): void => {
updateLink({ variables: { link: updatedLink } });
updateLink({ variables: { link: updatedLink } }).catch((error) => {
console.error('[LinkPage.updateLink]', error);
});
},
[updateLink]
);
Expand Down
4 changes: 4 additions & 0 deletions client/src/renderer/containers/TagPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const TAG_QUERY = gql`
query TagWithNotesQuery($tagId: ID!) {
tag(tagId: $tagId) {
_id
updatedAt
name
color
Expand Down Expand Up @@ -50,6 +51,7 @@ const UPDATE_TAG_MUTATION = gql`
mutation UpdateTagMutation($tag: InputTag!) {
updateTag(tag: $tag) {
_id
updatedAt
name
color
Expand Down Expand Up @@ -93,6 +95,8 @@ const TagPage: React.FC<{}> = () => {
(updatedTag): void => {
updateTag({
variables: { tag: updatedTag },
}).catch((error) => {
console.error('[TagPage.updateTag]', error);
});
},
[updateTag]
Expand Down
8 changes: 7 additions & 1 deletion client/src/renderer/containers/TextPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const TEXT_QUERY = gql`
text(textId: $textId) {
_id
createdAt
updatedAt
archivedAt
name
description
Expand All @@ -39,6 +40,7 @@ const UPDATE_TEXT_MUTATION = gql`
updateText(text: $text) {
_id
createdAt
updatedAt
archivedAt
name
description
Expand All @@ -65,7 +67,11 @@ const TextPage: React.FC = () => {
UpdateTextMutationVariables
>(UPDATE_TEXT_MUTATION);
const handleSubmit = useCallback(
(updatedText) => updateText({ variables: { text: updatedText } }),
(updatedText) => {
updateText({ variables: { text: updatedText } }).catch((error) => {
console.error('[TextPage.updateText]', error);
});
},
[updateText]
);

Expand Down
5 changes: 3 additions & 2 deletions client/src/renderer/generated/LinkQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
// ====================================================

export interface LinkQuery_link_tags {
__typename: "Tag";
__typename: 'Tag';
_id: string;
name: string;
color: string;
}

export interface LinkQuery_link {
__typename: "Link";
__typename: 'Link';
_id: string;
createdAt: any;
updatedAt: any;
archivedAt: any | null;
url: string;
name: string;
Expand Down
5 changes: 3 additions & 2 deletions client/src/renderer/generated/TextQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@
// ====================================================

export interface TextQuery_text_tags {
__typename: "Tag";
__typename: 'Tag';
_id: string;
name: string;
color: string;
}

export interface TextQuery_text {
__typename: "Text";
__typename: 'Text';
_id: string;
createdAt: any;
archivedAt: any | null;
updatedAt: any;
name: string;
description: string;
tags: TextQuery_text_tags[];
Expand Down
3 changes: 3 additions & 0 deletions client/src/renderer/reducers/links.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type TagObject = {
_id: string;
createdAt: number;
updatedAt: number;
name: string;
color: string;
};
Expand All @@ -12,5 +14,6 @@ export type NoteObject = {
description: string;
tags: Array<TagObject>;
createdAt: number; // todo: Date
updatedAt: number; // todo: Date
archivedAt: number | null; // todo: Date
};
3 changes: 3 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,22 @@ input InputLink {
domain: String
name: String
path: String
updatedAt: Date
url: String
}

input InputTag {
_id: ID!
color: String!
name: String!
updatedAt: Date
}

input InputText {
_id: ID!
description: String
name: String
updatedAt: Date
}

type Link implements BaseObject & INote {
Expand Down
18 changes: 18 additions & 0 deletions server/lib/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ const rootResolvers = {
throw new Error('Resource could not be found.')
}

if (tag.updatedAt > props.updatedAt) {
throw new Error(
'Tag has been changed externally since last sync, change rejected.',
)
}

Object.entries(props).forEach(([propName, propValue]) => {
tag[propName] = propValue
})
Expand All @@ -173,6 +179,12 @@ const rootResolvers = {
throw new Error('Resource could not be found.')
}

if (link.updatedAt > props.updatedAt) {
throw new Error(
'Note has been changed externally since last sync, change rejected.',
)
}

Object.entries(props).forEach(([propName, propValue]) => {
link[propName] = propValue
})
Expand All @@ -199,6 +211,12 @@ const rootResolvers = {
throw new Error('Resource could not be found.')
}

if (text.updatedAt > props.updatedAt) {
throw new Error(
'Note has been changed externally since last sync, change rejected.',
)
}

Object.entries(props).forEach(([propName, propValue]) => {
text[propName] = propValue
})
Expand Down
4 changes: 3 additions & 1 deletion server/lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default gql`
}
input InputTag {
_id: ID!
updatedAt: Date # todo: make mandatory in v0.21
name: String!
color: String!
}
Expand Down Expand Up @@ -88,6 +88,7 @@ export default gql`
}
input InputText {
_id: ID!
updatedAt: Date # todo: make mandatory in v0.21
name: String
description: String
}
Expand Down Expand Up @@ -115,6 +116,7 @@ export default gql`
}
input InputLink {
_id: ID!
updatedAt: Date # todo: make mandatory in v0.21
url: String
domain: String
path: String
Expand Down

0 comments on commit b1cd50c

Please sign in to comment.