Skip to content

Commit 4ceeffa

Browse files
committed
fix: impossible to publish an article
Also improve the workflow with drafts as they're now detected just like regular posts and are only re-published only if needed This closes #15
1 parent b2653c2 commit 4ceeffa

File tree

4 files changed

+64
-38
lines changed

4 files changed

+64
-38
lines changed

src/article.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ describe(`Article`, () => {
77
const relativePathToArticle = `./test/article.md`;
88

99
beforeEach(() => {
10-
article = new Article({
11-
id: 0,
12-
relativePathToArticle,
13-
repository,
14-
});
10+
article = new Article(
11+
{
12+
id: 0,
13+
relativePathToArticle,
14+
repository,
15+
},
16+
'private-dev-to-token',
17+
);
1518
});
1619

1720
describe(`Read`, () => {

src/article.ts

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { ArticleConfig, ArticleApi, ArticlePublishedStatus, UpdateStatus } from './dev-to-git.interface';
1+
import {
2+
ArticleConfig,
3+
ArticleApi,
4+
ArticlePublishedStatus,
5+
UpdateStatus,
6+
ArticleApiResponse,
7+
} from './dev-to-git.interface';
28
import got from 'got';
39
import fs from 'fs';
410
import extractFrontMatter from 'front-matter';
@@ -19,7 +25,24 @@ interface ImageToReplace {
1925
}
2026

2127
export class Article {
22-
constructor(private articleConfig: ArticleConfig) {}
28+
// dev.to API returns a maximum of 1000 articles but would by default return only 30
29+
// https://docs.dev.to/api/#tag/articles/paths/~1articles~1me~1all/get
30+
// instead of having to manage the pagination I think it's safe to assume people using
31+
// dev-to-git won't have more than 1000 articles for now
32+
// also note that we're using a property instead of a method here so that the result is
33+
// shared/reused for all the different articles with only 1 HTTP call
34+
private articles: Promise<Record<number, string>> = got(`https://dev.to/api/articles/me/all?per_page=1000`, {
35+
json: true,
36+
method: 'GET',
37+
headers: { 'api-key': this.token },
38+
}).then((res: got.Response<ArticleApiResponse[]>) =>
39+
res.body.reduce<Record<number, string>>((articlesMap, article) => {
40+
articlesMap[article.id] = article.body_markdown;
41+
return articlesMap;
42+
}, {}),
43+
);
44+
45+
constructor(private articleConfig: ArticleConfig, private token: string) {}
2346

2447
private updateLocalImageLinks(article: string): string {
2548
let searchImageResult;
@@ -55,14 +78,7 @@ export class Article {
5578
return this.updateLocalImageLinks(article);
5679
}
5780

58-
private fetchArticleBodyMarkdown(articleId: number): Promise<string | null> {
59-
return got(`https://dev.to/api/articles/${articleId}`, {
60-
json: true,
61-
method: 'GET',
62-
}).then((x: got.Response<ArticleApi>) => x.body.body_markdown);
63-
}
64-
65-
public async publishArticle(token: string): Promise<ArticlePublishedStatus> {
81+
public async publishArticle(): Promise<ArticlePublishedStatus> {
6682
const body: ArticleApi = {
6783
body_markdown: this.readArticleOnDisk(),
6884
};
@@ -80,34 +96,36 @@ export class Article {
8096

8197
let remoteArticleBodyMarkdown: string | null;
8298

83-
// if it's a draft, the article cannot be fetched so we just re-publish drafts
84-
if (frontMatter.published) {
85-
try {
86-
remoteArticleBodyMarkdown = await this.fetchArticleBodyMarkdown(this.articleConfig.id);
87-
} catch (error) {
88-
return {
89-
updateStatus: UpdateStatus.ERROR as UpdateStatus.ERROR,
90-
articleId: this.articleConfig.id,
91-
articleTitle: frontMatter.title,
92-
error,
93-
published: frontMatter.published,
94-
};
95-
}
99+
try {
100+
const articles: Record<number, string> = await this.articles;
101+
remoteArticleBodyMarkdown = articles[this.articleConfig.id];
96102

97-
if (remoteArticleBodyMarkdown && remoteArticleBodyMarkdown.trim() === body.body_markdown.trim()) {
98-
return {
99-
articleId: this.articleConfig.id,
100-
updateStatus: UpdateStatus.ALREADY_UP_TO_DATE as UpdateStatus.ALREADY_UP_TO_DATE,
101-
articleTitle: frontMatter.title,
102-
published: frontMatter.published,
103-
};
103+
if (!remoteArticleBodyMarkdown) {
104+
throw new Error();
104105
}
106+
} catch (error) {
107+
return {
108+
updateStatus: UpdateStatus.ERROR as UpdateStatus.ERROR,
109+
articleId: this.articleConfig.id,
110+
articleTitle: frontMatter.title,
111+
error,
112+
published: frontMatter.published,
113+
};
114+
}
115+
116+
if (remoteArticleBodyMarkdown && remoteArticleBodyMarkdown.trim() === body.body_markdown.trim()) {
117+
return {
118+
articleId: this.articleConfig.id,
119+
updateStatus: UpdateStatus.ALREADY_UP_TO_DATE as UpdateStatus.ALREADY_UP_TO_DATE,
120+
articleTitle: frontMatter.title,
121+
published: frontMatter.published,
122+
};
105123
}
106124

107125
return got(`https://dev.to/api/articles/${this.articleConfig.id}`, {
108126
json: true,
109127
method: 'PUT',
110-
headers: { 'api-key': token },
128+
headers: { 'api-key': this.token },
111129
body,
112130
})
113131
.then(() => ({

src/dev-to-git.interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ export interface ArticleApi {
1717
body_markdown: string;
1818
}
1919

20+
export interface ArticleApiResponse {
21+
id: number;
22+
body_markdown: string;
23+
}
24+
2025
export enum UpdateStatus {
2126
UPDATED = 'Updated',
2227
ALREADY_UP_TO_DATE = 'AlreadyUpToDate',

src/dev-to-git.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ export class DevToGit {
110110

111111
return Promise.all(
112112
articles.map(articleConf => {
113-
const article = new Article(articleConf);
114-
return article.publishArticle(this.configuration.devToToken);
113+
const article = new Article(articleConf, this.configuration.devToToken);
114+
return article.publishArticle();
115115
}),
116116
);
117117
}

0 commit comments

Comments
 (0)