Skip to content

Commit

Permalink
Add bot edit submission support (#529)
Browse files Browse the repository at this point in the history
  • Loading branch information
InfiniteStash authored Nov 7, 2022
1 parent 56cc516 commit e4f8ef7
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 13 deletions.
13 changes: 13 additions & 0 deletions frontend/src/components/editCard/EditCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { FC } from "react";
import { Card, Col, Row } from "react-bootstrap";
import { Link } from "react-router-dom";
import cx from "classnames";
import { faRobot } from "@fortawesome/free-solid-svg-icons";
import { Icon, Tooltip } from "src/components/fragments";

import { OperationEnum, EditFragment } from "src/graphql";

Expand Down Expand Up @@ -56,6 +58,17 @@ const EditCardComponent: FC<Props> = ({ edit, showVotes = false }) => {
) : (
<span>Deleted User</span>
)}
{edit.bot && (
<Tooltip
text="Edit submitted by an automated script"
delay={50}
placement="auto"
>
<span>
<Icon icon={faRobot} className="ms-2" />
</span>
</Tooltip>
)}
</div>
<div>
<b className="me-2">Created:</b>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/list/EditList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const EditListComponent: FC<EditsProps> = ({
selectedOperation,
selectedStatus,
selectedFavorite,
selectedBot,
} = useEditFilter({
sort,
direction,
Expand All @@ -62,6 +63,7 @@ const EditListComponent: FC<EditsProps> = ({
operation: selectedOperation,
user_id: userId,
is_favorite: selectedFavorite,
is_bot: selectedBot,
page,
per_page: PER_PAGE,
sort: selectedSort,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/graphql/fragments/EditFragment.gql
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fragment EditFragment on Edit {
target_type
operation
status
bot
applied
created
updated
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export type DraftSubmissionStatus = {
export type Edit = {
__typename: "Edit";
applied: Scalars["Boolean"];
bot: Scalars["Boolean"];
closed?: Maybe<Scalars["Time"]>;
comments: Array<EditComment>;
created: Scalars["Time"];
Expand Down Expand Up @@ -173,6 +174,8 @@ export type EditCommentInput = {
export type EditDetails = PerformerEdit | SceneEdit | StudioEdit | TagEdit;

export type EditInput = {
/** Edit submitted by an automated script. Requires bot permission */
bot?: InputMaybe<Scalars["Boolean"]>;
comment?: InputMaybe<Scalars["String"]>;
/** Not required for create type */
id?: InputMaybe<Scalars["ID"]>;
Expand All @@ -185,6 +188,8 @@ export type EditQueryInput = {
/** Filter by applied status */
applied?: InputMaybe<Scalars["Boolean"]>;
direction?: SortDirectionEnum;
/** Filter to bot edits only */
is_bot?: InputMaybe<Scalars["Boolean"]>;
/** Filter by favorite status */
is_favorite?: InputMaybe<Scalars["Boolean"]>;
/** Filter by operation */
Expand Down Expand Up @@ -1843,6 +1848,7 @@ export type EditFragment = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -3165,6 +3171,7 @@ export type ApplyEditMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -4374,6 +4381,7 @@ export type PerformerEditMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -5402,6 +5410,7 @@ export type PerformerEditUpdateMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -6465,6 +6474,7 @@ export type SceneEditMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -7493,6 +7503,7 @@ export type SceneEditUpdateMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -8520,6 +8531,7 @@ export type StudioEditMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -9548,6 +9560,7 @@ export type StudioEditUpdateMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -10575,6 +10588,7 @@ export type TagEditMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -11603,6 +11617,7 @@ export type TagEditUpdateMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -12764,6 +12779,7 @@ export type VoteMutation = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -14048,6 +14064,7 @@ export type EditQuery = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -15512,6 +15529,7 @@ export type EditsQuery = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down Expand Up @@ -16854,6 +16872,7 @@ export type QueryExistingSceneQuery = {
target_type: TargetTypeEnum;
operation: OperationEnum;
status: VoteStatusEnum;
bot: boolean;
applied: boolean;
created: string;
updated?: string | null;
Expand Down
19 changes: 17 additions & 2 deletions frontend/src/hooks/useEditFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface EditFilterProps {
status?: VoteStatusEnum;
operation?: OperationEnum;
favorite?: boolean;
bot?: boolean;
showFavoriteOption?: boolean;
defaultVoteStatus?: VoteStatusEnum | "all";
}
Expand All @@ -44,6 +45,7 @@ const useEditFilter = ({
status: fixedStatus,
operation: fixedOperation,
favorite: fixedFavorite,
bot: fixedBot,
showFavoriteOption = true,
defaultVoteStatus = "all",
}: EditFilterProps) => {
Expand All @@ -55,6 +57,7 @@ const useEditFilter = ({
status: { name: "status", type: "string", default: defaultVoteStatus },
type: { name: "type", type: "string", default: "" },
favorite: { name: "favorite", type: "string", default: "false" },
bot: { name: "bot", type: "string", default: "false" },
});

const sort = ensureEnum(EditSortEnum, params.sort);
Expand All @@ -63,13 +66,15 @@ const useEditFilter = ({
const status = resolveEnum(VoteStatusEnum, params.status, undefined);
const type = resolveEnum(TargetTypeEnum, params.type);
const favorite = params.favorite === "true";
const bot = params.bot === "true";

const selectedSort = fixedSort ?? sort;
const selectedDirection = fixedDirection ?? direction;
const selectedStatus = fixedStatus ?? status;
const selectedType = fixedType ?? type;
const selectedOperation = fixedOperation ?? operation;
const selectedFavorite = fixedFavorite ?? favorite;
const selectedBot = fixedBot ?? bot;

const enumToOptions = (e: Record<string, string>) =>
Object.keys(e).map((key) => (
Expand Down Expand Up @@ -154,10 +159,10 @@ const useEditFilter = ({
</Form.Select>
</Form.Group>
{showFavoriteOption && (
<Form.Group controlId="favorite">
<Form.Group controlId="favorite" className="text-center">
<Form.Label>Favorites</Form.Label>
<Form.Check
className="ms-3 mt-2"
className="mt-2"
type="switch"
defaultChecked={favorite}
onChange={(e) =>
Expand All @@ -166,6 +171,15 @@ const useEditFilter = ({
/>
</Form.Group>
)}
<Form.Group controlId="bot" className="text-center ms-3">
<Form.Label>Bot Edits</Form.Label>
<Form.Check
className="mt-2"
type="switch"
defaultChecked={bot}
onChange={(e) => setParams("bot", e.currentTarget.checked.toString())}
/>
</Form.Group>
</Form>
);

Expand All @@ -177,6 +191,7 @@ const useEditFilter = ({
selectedStatus,
selectedOperation,
selectedFavorite,
selectedBot,
};
};

Expand Down
5 changes: 5 additions & 0 deletions graphql/schema/types/edit.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Edit {
"""Objects to merge with the target. Only applicable to merges"""
merge_sources: [EditTarget!]!
operation: OperationEnum!
bot: Boolean!
details: EditDetails
"""Previous state of fields being modified - null if operation is create or delete."""
old_details: EditDetails
Expand All @@ -84,6 +85,8 @@ input EditInput {
"""Only required for merge type"""
merge_source_ids: [ID!]
comment: String
"""Edit submitted by an automated script. Requires bot permission"""
bot: Boolean
}

input EditVoteInput {
Expand Down Expand Up @@ -124,6 +127,8 @@ input EditQueryInput {
target_id: ID
"""Filter by favorite status"""
is_favorite: Boolean
"""Filter to bot edits only"""
is_bot: Boolean

page: Int! = 1
per_page: Int! = 25
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func validateAdmin(ctx context.Context) error {
return user.ValidateRole(ctx, models.RoleEnumAdmin)
}

func validateBot(ctx context.Context) error {
return user.ValidateRole(ctx, models.RoleEnumBot)
}

func validateUser(ctx context.Context, userID uuid.UUID) error {
return user.ValidateOwner(ctx, userID)
}
Expand Down
27 changes: 24 additions & 3 deletions pkg/api/resolver_mutation_edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
var ErrUnauthorizedUpdate = fmt.Errorf("Only the creator can update edits")
var ErrAlreadyUpdated = fmt.Errorf("Edits can only be updated once")
var ErrClosedEdit = fmt.Errorf("Votes can only be cast on pending edits")
var ErrUnauthorizedBot = fmt.Errorf("You do not have permission to submit bot edits")

func (r *mutationResolver) SceneEdit(ctx context.Context, input models.SceneEditInput) (*models.Edit, error) {
UUID, err := uuid.NewV4()
Expand All @@ -22,12 +23,14 @@ func (r *mutationResolver) SceneEdit(ctx context.Context, input models.SceneEdit
}

currentUser := getCurrentUser(ctx)
if err := validateBotEdit(ctx, input.Edit); err != nil {
return nil, err
}

// create the edit
newEdit := models.NewEdit(UUID, currentUser, models.TargetTypeEnumScene, input.Edit)

fac := r.getRepoFactory(ctx)

err = fac.WithTxn(func() error {
p := edit.Scene(fac, newEdit)
if err := p.Edit(input, wasFieldIncludedFunc(ctx)); err != nil {
Expand Down Expand Up @@ -102,11 +105,13 @@ func (r *mutationResolver) StudioEdit(ctx context.Context, input models.StudioEd

// create the edit
currentUser := getCurrentUser(ctx)
if err := validateBotEdit(ctx, input.Edit); err != nil {
return nil, err
}

newEdit := models.NewEdit(UUID, currentUser, models.TargetTypeEnumStudio, input.Edit)

fac := r.getRepoFactory(ctx)

err = fac.WithTxn(func() error {
p := edit.Studio(fac, newEdit)
if err := p.Edit(input, wasFieldIncludedFunc(ctx)); err != nil {
Expand Down Expand Up @@ -175,11 +180,13 @@ func (r *mutationResolver) TagEdit(ctx context.Context, input models.TagEditInpu

// create the edit
currentUser := getCurrentUser(ctx)
if err := validateBotEdit(ctx, input.Edit); err != nil {
return nil, err
}

newEdit := models.NewEdit(UUID, currentUser, models.TargetTypeEnumTag, input.Edit)

fac := r.getRepoFactory(ctx)

err = fac.WithTxn(func() error {
p := edit.Tag(fac, newEdit)
if err := p.Edit(input, wasFieldIncludedFunc(ctx)); err != nil {
Expand Down Expand Up @@ -248,8 +255,12 @@ func (r *mutationResolver) PerformerEdit(ctx context.Context, input models.Perfo

// create the edit
currentUser := getCurrentUser(ctx)
if err := validateBotEdit(ctx, input.Edit); err != nil {
return nil, err
}

newEdit := models.NewEdit(UUID, currentUser, models.TargetTypeEnumPerformer, input.Edit)

fac := r.getRepoFactory(ctx)
err = fac.WithTxn(func() error {
p := edit.Performer(fac, newEdit)
Expand Down Expand Up @@ -441,3 +452,13 @@ func (r *mutationResolver) ApplyEdit(ctx context.Context, input models.ApplyEdit

return edit.ApplyEdit(fac, input.ID, true)
}

func validateBotEdit(ctx context.Context, input *models.EditInput) error {
if input.Bot != nil && *input.Bot {
if err := validateBot(ctx); err != nil {
return ErrUnauthorizedBot
}
}

return nil
}
2 changes: 1 addition & 1 deletion pkg/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/jmoiron/sqlx"
)

var appSchemaVersion uint = 29
var appSchemaVersion uint = 30

var databaseProviders map[string]databaseProvider

Expand Down
2 changes: 2 additions & 0 deletions pkg/database/migrations/postgres/30_edit_bot.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE "edits"
ADD COLUMN "bot" BOOLEAN NOT NULL DEFAULT False;
Loading

0 comments on commit e4f8ef7

Please sign in to comment.