Skip to content

Commit ab8f85b

Browse files
committed
feat: refresh article content on update
1 parent 9b3c611 commit ab8f85b

File tree

7 files changed

+111
-36
lines changed

7 files changed

+111
-36
lines changed

internal/model/article.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,15 @@ func (form ArticleCreateForm) TruncatedTitle() string {
6464

6565
// ArticleUpdateForm structure definition
6666
type ArticleUpdateForm struct {
67-
ID uint
68-
Title *string
69-
Text *string
70-
CategoryID *uint
71-
Status *string
72-
Stars *int
67+
ID uint
68+
Title *string
69+
Text *string
70+
CategoryID *uint
71+
Status *string
72+
Stars *int
73+
RefreshContent *bool
74+
Image *string
75+
HTML *string
7376
}
7477

7578
// CreatedArticlesResponse structure definition

internal/schema/article/mutations.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ var updateArticleMutationField = &graphql.Field{
3333
"stars": &graphql.ArgumentConfig{
3434
Type: graphql.Int,
3535
},
36+
"refresh": &graphql.ArgumentConfig{
37+
Type: graphql.Boolean,
38+
},
3639
},
3740
Resolve: updateArticleResolver,
3841
}
@@ -44,12 +47,13 @@ func updateArticleResolver(p graphql.ResolveParams) (interface{}, error) {
4447
}
4548

4649
form := model.ArticleUpdateForm{
47-
ID: *id,
48-
Title: utils.ParseGraphQLArgument[string](p.Args, "title"),
49-
Text: utils.ParseGraphQLArgument[string](p.Args, "text"),
50-
CategoryID: utils.ParseGraphQLID(p.Args, "category_id"),
51-
Status: utils.ParseGraphQLArgument[string](p.Args, "status"),
52-
Stars: utils.ParseGraphQLArgument[int](p.Args, "stars"),
50+
ID: *id,
51+
Title: utils.ParseGraphQLArgument[string](p.Args, "title"),
52+
Text: utils.ParseGraphQLArgument[string](p.Args, "text"),
53+
CategoryID: utils.ParseGraphQLID(p.Args, "category_id"),
54+
Status: utils.ParseGraphQLArgument[string](p.Args, "status"),
55+
Stars: utils.ParseGraphQLArgument[int](p.Args, "stars"),
56+
RefreshContent: utils.ParseGraphQLArgument[bool](p.Args, "refresh"),
5357
}
5458

5559
article, err := service.Lookup().UpdateArticle(p.Context, form)

internal/service/articles_update.go

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func (reg *Registry) UpdateArticle(ctx context.Context, form model.ArticleUpdate
2323
logger := reg.logger.With().Uint("uid", uid).Uint("id", form.ID).Logger()
2424

2525
// Validate update form
26-
if err := validateArticleUpdateForm(form); err != nil {
26+
if err := validateArticleUpdateForm(&form); err != nil {
2727
logger.Info().Err(err).Msg(unableToUpdateArticleErrorMsg)
2828
return nil, err
2929
}
@@ -38,10 +38,17 @@ func (reg *Registry) UpdateArticle(ctx context.Context, form model.ArticleUpdate
3838
}
3939

4040
// Update loggers values
41-
logger = addLoggerContextForUpdateArticle(logger, form)
41+
updateLoggerContextWithUpdateForm(&logger, &form)
42+
logger.Debug().Msg("updating article...")
43+
44+
// Refresh HTML content if asked
45+
if form.RefreshContent != nil && *form.RefreshContent {
46+
if err := reg.refreshArticleContent(ctx, &form); err != nil {
47+
return nil, err
48+
}
49+
}
4250

4351
// Update article
44-
logger.Debug().Msg("updating article...")
4552
article, err := reg.db.UpdateArticleForUser(uid, form)
4653
if err != nil {
4754
logger.Err(err).Msg(unableToUpdateArticleErrorMsg)
@@ -61,42 +68,74 @@ func (reg *Registry) UpdateArticle(ctx context.Context, form model.ArticleUpdate
6168
return article, nil
6269
}
6370

64-
func addLoggerContextForUpdateArticle(logger zerolog.Logger, form model.ArticleUpdateForm) zerolog.Logger {
65-
ctx := logger.With()
66-
if form.CategoryID != nil {
67-
ctx = ctx.Uint("category_id", *form.CategoryID)
71+
func (reg *Registry) refreshArticleContent(ctx context.Context, form *model.ArticleUpdateForm) error {
72+
article, err := reg.GetArticle(ctx, form.ID)
73+
if err != nil {
74+
return err
6875
}
69-
if form.Stars != nil {
70-
ctx = ctx.Int("stars", *form.Stars)
76+
77+
if article.URL == nil || *article.URL == "" {
78+
// nothing to refresh
79+
return nil
7180
}
72-
if form.Status != nil {
73-
ctx = ctx.Str("status", *form.Status)
81+
82+
logger := reg.logger.With().Uint("uid", article.UserID).Uint("id", form.ID).Logger()
83+
logger.Debug().Msg("refreshing article content...")
84+
page, err := reg.webScraper.Scrap(ctx, *article.URL)
85+
if err != nil {
86+
return err
7487
}
75-
if form.Title != nil {
76-
ctx = ctx.Str("title", utils.Truncate(*form.Title, 24))
88+
if form.Title == nil || *form.Title == "" {
89+
form.Title = &page.Title
7790
}
78-
if form.Text != nil {
79-
ctx = ctx.Str("text", utils.Truncate(*form.Text, 24))
91+
if form.Text == nil || *form.Text == "" {
92+
form.Text = &page.Text
8093
}
81-
return ctx.Logger()
94+
form.HTML = &page.HTML
95+
form.Image = &page.Image
96+
return nil
97+
}
98+
99+
func updateLoggerContextWithUpdateForm(logger *zerolog.Logger, form *model.ArticleUpdateForm) {
100+
logger.UpdateContext(func(ctx zerolog.Context) zerolog.Context {
101+
if form.CategoryID != nil {
102+
ctx = ctx.Uint("category_id", *form.CategoryID)
103+
}
104+
if form.Stars != nil {
105+
ctx = ctx.Int("stars", *form.Stars)
106+
}
107+
if form.Status != nil {
108+
ctx = ctx.Str("status", *form.Status)
109+
}
110+
if form.Title != nil {
111+
ctx = ctx.Str("title", utils.Truncate(*form.Title, 24))
112+
}
113+
if form.Text != nil {
114+
ctx = ctx.Str("text", utils.Truncate(*form.Text, 24))
115+
}
116+
if form.RefreshContent != nil {
117+
ctx = ctx.Bool("refresh", *form.RefreshContent)
118+
}
119+
return ctx
120+
})
82121
}
83122

84-
func validateArticleUpdateForm(form model.ArticleUpdateForm) error {
123+
func validateArticleUpdateForm(form *model.ArticleUpdateForm) error {
85124
validations := new(validator.FieldsValidator)
86125
validations.Validate("stars", func() bool {
87126
return form.Stars == nil || *form.Stars <= 5
88127
})
89128
validations.Validate("title", func() bool {
90129
if form.Title != nil {
91130
l := len(strings.TrimSpace(*form.Title))
92-
return l > 0 && l <= 256
131+
return l <= 256
93132
}
94133
return true
95134
})
96135
validations.Validate("text", func() bool {
97136
if form.Text != nil {
98137
l := len(strings.TrimSpace(*form.Text))
99-
return l >= 0 && l <= 512
138+
return l <= 512
100139
}
101140
return true
102141
})

internal/service/scripting.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func (reg *Registry) execOtherOperations(ctx context.Context, ops scripting.Oper
115115
}
116116
}
117117
if status != "" {
118+
// TODO move this logic to execSetOperations step
118119
update := model.ArticleUpdateForm{
119120
ID: article.ID,
120121
Status: &status,

ui/src/articles/components/EditArticleForm.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import { useMutation } from '@apollo/client'
33
import { useFormState } from 'react-use-form-state'
44

55
import { useMessage } from '../../contexts'
6-
import { Button, CategoriesOptions, ErrorPanel, FormInputField, FormSelectField, FormTextareaField, Loader, Panel } from '../../components'
6+
import { Button, CategoriesOptions, ErrorPanel, FormCheckboxField, FormInputField, FormSelectField, FormTextareaField, Loader, Panel } from '../../components'
77
import { getGQLError, isValidForm } from '../../helpers'
88
import { Article, UpdateArticleRequest, UpdateArticleResponse } from '../models'
9-
import { UpdateArticle } from '../queries'
9+
import { UpdateFullArticle } from '../queries'
1010
import { updateCacheAfterUpdate } from '../cache'
1111

1212
interface EditArticleFormFields {
1313
title: string
1414
text: string
1515
category_id?: number
16+
refresh: boolean
1617
}
1718

1819
interface Props {
@@ -25,12 +26,13 @@ export const EditArticleForm = ({ article, onSuccess, onCancel }: Props) => {
2526
const [loading, setLoading] = useState(false)
2627
const [errorMessage, setErrorMessage] = useState<string | null>(null)
2728
const { showMessage } = useMessage()
28-
const [formState, { text, textarea, select }] = useFormState<EditArticleFormFields>({
29+
const [formState, { text, textarea, select, checkbox }] = useFormState<EditArticleFormFields>({
2930
title: article.title,
3031
text: article.text,
3132
category_id: article.category?.id,
33+
refresh: false,
3234
})
33-
const [editArticleMutation] = useMutation<UpdateArticleResponse, UpdateArticleRequest>(UpdateArticle)
35+
const [editArticleMutation] = useMutation<UpdateArticleResponse, UpdateArticleRequest>(UpdateFullArticle)
3436

3537
const editArticle = useCallback(
3638
async (form: EditArticleFormFields) => {
@@ -76,12 +78,13 @@ export const EditArticleForm = ({ article, onSuccess, onCancel }: Props) => {
7678
<section>
7779
{errorMessage != null && <ErrorPanel title="Unable to edit article">{errorMessage}</ErrorPanel>}
7880
<form onSubmit={handleOnSubmit}>
79-
<FormInputField label="Title" {...text('title')} error={formState.errors.title} required pattern=".*\S+.*" maxLength={256} autoFocus />
81+
<FormInputField label="Title" {...text('title')} error={formState.errors.title} pattern=".*\S+.*" maxLength={256} autoFocus />
8082
<FormTextareaField label="Text" {...textarea('text')} error={formState.errors.text} maxLength={512} />
8183
<FormSelectField label="Category" {...select('category_id')} error={formState.errors.category_id}>
8284
<option>Optional category</option>
8385
<CategoriesOptions />
8486
</FormSelectField>
87+
<FormCheckboxField label="Refresh content" {...checkbox('refresh')} />
8588
</form>
8689
</section>
8790
<footer>

ui/src/articles/models.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface UpdateArticleRequest {
5858
status?: ArticleStatus
5959
stars?: number
6060
category_id?: number
61+
refresh?: boolean
6162
}
6263

6364
export interface UpdateArticleResponse {

ui/src/articles/queries.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,30 @@ export const UpdateArticle = gql`
113113
}
114114
`
115115

116+
export const UpdateFullArticle = gql`
117+
mutation updateArticle($id: ID!, $title: String, $text: String, $category_id: ID, $status: status, $stars: Int, $refresh: Boolean) {
118+
updateArticle(id: $id, title: $title, text: $text, category_id: $category_id, status: $status, stars: $stars, refresh: $refresh) {
119+
article {
120+
id
121+
title
122+
text
123+
html
124+
url
125+
status
126+
stars
127+
category {
128+
id
129+
inbox
130+
}
131+
updated_at
132+
}
133+
_inbox
134+
_to_read
135+
_starred
136+
}
137+
}
138+
`
139+
116140
export const SendArticleToOutgoingWebhook = gql`
117141
mutation sendArticleToOutgoingWebhook($id: ID!, $alias: String!) {
118142
sendArticleToOutgoingWebhook(id: $id, alias: $alias) {

0 commit comments

Comments
 (0)