@griddo/cx 1.75.178 → 1.75.180
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -14
- package/build/index.js +49 -0
- package/gatsby-browser.tsx +140 -0
- package/gatsby-config.ts +40 -0
- package/gatsby-node.ts +130 -0
- package/gatsby-ssr.tsx +63 -0
- package/index.js +4 -0
- package/package.json +119 -100
- package/scripts/build-reset.ts +9 -0
- package/scripts/griddo-exporter.ts +302 -0
- package/src/components/Head.tsx +207 -0
- package/src/components/template.tsx +90 -0
- package/src/components/types.ts +35 -0
- package/src/components/utils.ts +138 -0
- package/src/html.tsx +31 -0
- package/src/services/auth.ts +64 -0
- package/src/services/distributors.ts +153 -0
- package/src/services/domains.ts +27 -0
- package/src/services/navigation.ts +169 -0
- package/src/services/robots.ts +64 -0
- package/src/services/settings.ts +52 -0
- package/src/services/sites.ts +199 -0
- package/src/services/store-with-for-loop.ts +513 -0
- package/src/services/store-with-promise-all.ts +515 -0
- package/src/services/store.ts +513 -0
- package/src/types/api.ts +155 -0
- package/src/types/global.ts +72 -0
- package/src/types/navigation.ts +25 -0
- package/src/types/pages.ts +145 -0
- package/src/types/sites.ts +64 -0
- package/src/types/templates.ts +11 -0
- package/src/utils/api.ts +326 -0
- package/src/utils/cache.ts +114 -0
- package/src/utils/domains.ts +32 -0
- package/src/utils/folders.ts +147 -0
- package/src/utils/instance.ts +71 -0
- package/src/utils/pages.ts +534 -0
- package/src/utils/searches.ts +102 -0
- package/src/utils/shared.ts +131 -0
- package/src/utils/sites.ts +280 -0
- package/.babelrc +0 -7
- package/LICENSE +0 -1
- package/gatsby-browser.js +0 -115
- package/gatsby-config.js +0 -38
- package/gatsby-node.js +0 -273
- package/gatsby-ssr.js +0 -43
- package/scripts/griddo-exporter.js +0 -133
- package/src/api/index.js +0 -172
- package/src/components/template.js +0 -261
- package/src/html.js +0 -30
- package/src/images/readme.md +0 -1
- package/src/scripts/build-reset.js +0 -12
- package/src/services/auth.js +0 -44
- package/src/services/distributors.js +0 -85
- package/src/services/domains.js +0 -16
- package/src/services/navigation.js +0 -104
- package/src/services/robots.js +0 -37
- package/src/services/settings.js +0 -25
- package/src/services/sites.js +0 -110
- package/src/utils/cache.js +0 -70
- package/src/utils/component-lib-helpers.js +0 -56
- package/src/utils/dataLayer.js +0 -47
- package/src/utils/delay.js +0 -5
- package/src/utils/domains.js +0 -24
- package/src/utils/folders.js +0 -123
- package/src/utils/helpers.js +0 -144
- package/src/utils/index.js +0 -44
- package/src/utils/package.js +0 -20
- package/src/utils/pages.js +0 -236
- package/src/utils/searches.js +0 -62
- package/src/utils/sites.js +0 -207
- package/static/griddo-full-logo.png +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Core, Fields } from "@griddo/core";
|
|
3
|
+
|
|
4
|
+
export type StoreMode = "memory" | "file";
|
|
5
|
+
|
|
6
|
+
export interface Settings {
|
|
7
|
+
apiVersion?: string;
|
|
8
|
+
avoidCanonicalsOnSitemaps?: boolean;
|
|
9
|
+
avoidDebugMetas?: boolean;
|
|
10
|
+
avoidHrefLangsOnCanonicals?: boolean;
|
|
11
|
+
avoidHrefLangXDefault?: boolean;
|
|
12
|
+
avoidSelfReferenceCanonicals?: boolean;
|
|
13
|
+
cloudinaryName?: string;
|
|
14
|
+
forceMenuLinksLanguage?: boolean;
|
|
15
|
+
globalLogoBig?: string;
|
|
16
|
+
globalLogoMini?: string;
|
|
17
|
+
lastDBCheckVersion?: string;
|
|
18
|
+
schemasTimestamp?: string;
|
|
19
|
+
schemasVersion?: string;
|
|
20
|
+
showBasicMetaRobots?: boolean;
|
|
21
|
+
siteLogoBig?: string;
|
|
22
|
+
siteLogoMini?: string;
|
|
23
|
+
skipReviewOnPublish?: boolean;
|
|
24
|
+
useMetaTitle?: boolean;
|
|
25
|
+
welcomeText1?: string;
|
|
26
|
+
welcomeText2?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** ??? */
|
|
30
|
+
export type Petition = Record<string, unknown>;
|
|
31
|
+
|
|
32
|
+
export interface PostSearchInfoProps {
|
|
33
|
+
title?: string;
|
|
34
|
+
description: string;
|
|
35
|
+
image: string;
|
|
36
|
+
pageId?: number;
|
|
37
|
+
siteId: number;
|
|
38
|
+
content: string;
|
|
39
|
+
languageId?: number;
|
|
40
|
+
template?: string | null | number;
|
|
41
|
+
url?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface FetchDataProps {
|
|
45
|
+
page: Core.Page;
|
|
46
|
+
component: {
|
|
47
|
+
data: Fields.Reference<unknown>;
|
|
48
|
+
};
|
|
49
|
+
cached: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type Domains = Array<{
|
|
53
|
+
id: number;
|
|
54
|
+
slug: string;
|
|
55
|
+
url: string;
|
|
56
|
+
}>;
|
|
57
|
+
|
|
58
|
+
export type StartPageInfo = {
|
|
59
|
+
/** Site hashes to publish. */
|
|
60
|
+
publishHashes: Array<string>;
|
|
61
|
+
/** Current site hash. */
|
|
62
|
+
siteHash: string | null;
|
|
63
|
+
/** Site hashes to unpublish. */
|
|
64
|
+
unpublishHashes: Array<string>;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/** Describes the type of build process data object. */
|
|
68
|
+
export type BuildProcessData = Record<string, StartPageInfo>;
|
|
69
|
+
|
|
70
|
+
export type Robot = { path: string; content: string };
|
|
71
|
+
|
|
72
|
+
export type Robots = Array<Robot>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Describes a Griddo Header */
|
|
2
|
+
export type Header = {
|
|
3
|
+
component: "Header";
|
|
4
|
+
id: number;
|
|
5
|
+
isDefault: boolean;
|
|
6
|
+
language: number;
|
|
7
|
+
navigationLanguages: Array<{ navigationId: number }>;
|
|
8
|
+
setAsDefault: boolean;
|
|
9
|
+
theme: null | string;
|
|
10
|
+
title: string;
|
|
11
|
+
type: "header";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/** Describes a Griddo Footer */
|
|
15
|
+
export type Footer = {
|
|
16
|
+
component: "Footer";
|
|
17
|
+
id: number;
|
|
18
|
+
isDefault: boolean;
|
|
19
|
+
language: number;
|
|
20
|
+
navigationLanguages: Array<{ navigationId: number }>;
|
|
21
|
+
setAsDefault: boolean;
|
|
22
|
+
theme: null | string;
|
|
23
|
+
title: string;
|
|
24
|
+
type: "header";
|
|
25
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Core, Fields } from "@griddo/core";
|
|
3
|
+
import type { SocialsResponse } from "./api";
|
|
4
|
+
import type { Settings } from "./global";
|
|
5
|
+
import type { Site } from "./sites";
|
|
6
|
+
|
|
7
|
+
// TODO: In @griddo/core the type Core.Page has header/footer as React-Types,
|
|
8
|
+
// but API return `number | null`.
|
|
9
|
+
export type APIPageObject = Core.Page & {
|
|
10
|
+
header: number | null;
|
|
11
|
+
footer: number | null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CleanPage = Core.Page & {
|
|
15
|
+
isRoot: boolean;
|
|
16
|
+
defaultLang?: Core.SiteLanguage | undefined;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type RenderPage = Core.Page & {
|
|
20
|
+
isRoot?: boolean;
|
|
21
|
+
multiPageElements: MultiPageElements;
|
|
22
|
+
defaultLang?: Core.SiteLanguage;
|
|
23
|
+
header: number;
|
|
24
|
+
footer: number;
|
|
25
|
+
//
|
|
26
|
+
id: number;
|
|
27
|
+
path: string;
|
|
28
|
+
component: "Page";
|
|
29
|
+
template_id: string;
|
|
30
|
+
mode: "list";
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type GriddoSinglePage = Core.Page & {
|
|
34
|
+
defaultLang?: Core.SiteLanguage;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type GriddoListPage = Core.Page & {
|
|
38
|
+
page: APIPageObject;
|
|
39
|
+
pages: Array<Array<Fields.QueriedDataItem>>;
|
|
40
|
+
isRoot?: boolean;
|
|
41
|
+
defaultLang?: Core.SiteLanguage;
|
|
42
|
+
template: {
|
|
43
|
+
[key: string]: any;
|
|
44
|
+
type: "template";
|
|
45
|
+
templateType: string;
|
|
46
|
+
activeSectionSlug: string;
|
|
47
|
+
activeSectionBase: string;
|
|
48
|
+
};
|
|
49
|
+
totalQueriedItems: Array<Fields.QueriedDataItem>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type GriddoMultiPage = Core.Page & {
|
|
53
|
+
header: number | null;
|
|
54
|
+
footer: number | null;
|
|
55
|
+
isRoot?: boolean;
|
|
56
|
+
multiPageElements: MultiPageElements;
|
|
57
|
+
defaultLang?: Core.SiteLanguage | undefined;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export interface AdditionalInfo {
|
|
61
|
+
baseUrl: string;
|
|
62
|
+
BUILD_MODE?: string;
|
|
63
|
+
cloudinaryName?: string;
|
|
64
|
+
griddoVersion: string;
|
|
65
|
+
instance?: string;
|
|
66
|
+
publicBaseUrl: string;
|
|
67
|
+
siteLangs: Array<Core.SiteLanguage>;
|
|
68
|
+
siteMetadata: Site["siteMetadata"];
|
|
69
|
+
siteSlug: string;
|
|
70
|
+
socials: SocialsResponse;
|
|
71
|
+
siteOptions: Pick<
|
|
72
|
+
Settings,
|
|
73
|
+
| "useMetaTitle"
|
|
74
|
+
| "showBasicMetaRobots"
|
|
75
|
+
| "avoidHrefLangsOnCanonicals"
|
|
76
|
+
| "avoidSelfReferenceCanonicals"
|
|
77
|
+
| "avoidHrefLangXDefault"
|
|
78
|
+
| "avoidDebugMetas"
|
|
79
|
+
>;
|
|
80
|
+
siteScript: string;
|
|
81
|
+
theme: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface PageAdditionalInfo extends AdditionalInfo {
|
|
85
|
+
navigations: {
|
|
86
|
+
header: Record<string, unknown> | null;
|
|
87
|
+
footer: Record<string, unknown> | null;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type GatsbyPageObject = {
|
|
92
|
+
matchPath?: string;
|
|
93
|
+
path: string;
|
|
94
|
+
component: string;
|
|
95
|
+
/** Page size in bytes */
|
|
96
|
+
size: number;
|
|
97
|
+
context: {
|
|
98
|
+
// Page
|
|
99
|
+
BUILD_MODE?: string;
|
|
100
|
+
cloudinaryName?: string;
|
|
101
|
+
footer: Record<string, unknown> | null;
|
|
102
|
+
fullPath: Core.Page["fullPath"];
|
|
103
|
+
griddoVersion: string;
|
|
104
|
+
header: Record<string, unknown> | null;
|
|
105
|
+
id?: number;
|
|
106
|
+
languageId: number;
|
|
107
|
+
locale?: string;
|
|
108
|
+
openGraph: {
|
|
109
|
+
description?: string;
|
|
110
|
+
image?: string | null;
|
|
111
|
+
title?: string;
|
|
112
|
+
twitterImage?: string | null;
|
|
113
|
+
type: "website";
|
|
114
|
+
};
|
|
115
|
+
pageMetadata: {
|
|
116
|
+
canonical?: string | undefined;
|
|
117
|
+
description?: string;
|
|
118
|
+
follow?: "follow" | "nofollow";
|
|
119
|
+
index: "index" | "noindex";
|
|
120
|
+
locale?: string;
|
|
121
|
+
metasAdvanced?: string;
|
|
122
|
+
pageLanguages?: Core.Page["pageLanguages"];
|
|
123
|
+
title?: string;
|
|
124
|
+
translate?: "notranslate" | "";
|
|
125
|
+
url?: string;
|
|
126
|
+
};
|
|
127
|
+
siteMetadata: Site["siteMetadata"];
|
|
128
|
+
theme: string;
|
|
129
|
+
title: string;
|
|
130
|
+
siteLangs: Array<Core.SiteLanguage>;
|
|
131
|
+
siteOptions: AdditionalInfo["siteOptions"];
|
|
132
|
+
siteScript: string;
|
|
133
|
+
socials: SocialsResponse;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export type MultiPageElements = Array<{
|
|
138
|
+
component: string;
|
|
139
|
+
title: string | Required<Fields.Heading>;
|
|
140
|
+
elements: Array<Record<string, unknown>>;
|
|
141
|
+
componentModules: Array<Record<string, unknown>>;
|
|
142
|
+
sectionSlug: string;
|
|
143
|
+
metaTitle: string;
|
|
144
|
+
metaDescription: string;
|
|
145
|
+
}>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Core } from "@griddo/core";
|
|
3
|
+
import type { AllPagesResponse, SocialsResponse } from "./api";
|
|
4
|
+
import type { Footer, Header } from "./navigation";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Describe a Griddo site object from API.
|
|
8
|
+
* This takes some type props from Core.Site which is a Site for the Gatsby template.tsx.
|
|
9
|
+
*/
|
|
10
|
+
export interface Site
|
|
11
|
+
extends Required<
|
|
12
|
+
Pick<
|
|
13
|
+
Core.Site,
|
|
14
|
+
| "bigAvatar"
|
|
15
|
+
| "favicon"
|
|
16
|
+
| "home"
|
|
17
|
+
| "id"
|
|
18
|
+
| "isPublished"
|
|
19
|
+
| "timezone"
|
|
20
|
+
| "thumbnail"
|
|
21
|
+
| "theme"
|
|
22
|
+
| "socials"
|
|
23
|
+
| "slug"
|
|
24
|
+
| "smallAvatar"
|
|
25
|
+
| "modified"
|
|
26
|
+
| "name"
|
|
27
|
+
| "siteMetadata"
|
|
28
|
+
>
|
|
29
|
+
> {
|
|
30
|
+
author: string;
|
|
31
|
+
deleted: number;
|
|
32
|
+
domains: Array<Record<string, string>>;
|
|
33
|
+
footers: Array<Footer>;
|
|
34
|
+
hash: string | null;
|
|
35
|
+
headers: Array<Header>;
|
|
36
|
+
languages: Array<Core.SiteLanguage>; // in Core is Array<number>
|
|
37
|
+
languageSites: Array<number>;
|
|
38
|
+
navigationModules: { header?: string; footer?: string } | null;
|
|
39
|
+
pages: Array<number>;
|
|
40
|
+
published: string;
|
|
41
|
+
rendering: boolean;
|
|
42
|
+
renderingHours: number;
|
|
43
|
+
shouldBeUpdated: boolean;
|
|
44
|
+
siteScript: string;
|
|
45
|
+
smallAvatar: string;
|
|
46
|
+
updated: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface SiteData {
|
|
50
|
+
siteInfo: Site;
|
|
51
|
+
validPagesIds: number[];
|
|
52
|
+
siteHash: string | null;
|
|
53
|
+
unpublishHashes: string[];
|
|
54
|
+
siteLangs: Array<Core.SiteLanguage>;
|
|
55
|
+
defaultLang: Core.SiteLanguage | undefined;
|
|
56
|
+
headers: Array<Header>;
|
|
57
|
+
footers: Array<Footer>;
|
|
58
|
+
socials: SocialsResponse;
|
|
59
|
+
sitePages: AllPagesResponse;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type SiteHash = string | null;
|
|
63
|
+
|
|
64
|
+
export type HashSites = Record<string, number | string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type { Core, Fields } from "@griddo/core";
|
|
3
|
+
|
|
4
|
+
/** Describe a template object from a Griddo Page */
|
|
5
|
+
export type Template = Core.Page["template"];
|
|
6
|
+
|
|
7
|
+
/** Describes a template with distributor data */
|
|
8
|
+
export type TemplateWithDistributor = Template & {
|
|
9
|
+
queriedItems?: Fields.QueriedData<unknown>;
|
|
10
|
+
itemsPerPage?: number;
|
|
11
|
+
};
|
package/src/utils/api.ts
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
import type {
|
|
3
|
+
APIResponses,
|
|
4
|
+
Error,
|
|
5
|
+
GetAPI,
|
|
6
|
+
PostAPI,
|
|
7
|
+
PutAPI,
|
|
8
|
+
} from "../types/api";
|
|
9
|
+
|
|
10
|
+
// External libraries
|
|
11
|
+
import axios from "axios";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import dotenv from "dotenv";
|
|
14
|
+
|
|
15
|
+
// Services
|
|
16
|
+
import { AuthService } from "../services/auth";
|
|
17
|
+
|
|
18
|
+
// Utils
|
|
19
|
+
import { getCache, saveCache } from "./cache";
|
|
20
|
+
import { delay, getSafeSiteId, logInfo, msToSec } from "./shared";
|
|
21
|
+
|
|
22
|
+
dotenv.config();
|
|
23
|
+
|
|
24
|
+
// Envs
|
|
25
|
+
const {
|
|
26
|
+
env: { RETRY_WAIT_SECONDS = "4", RETRY_ATTEMPTS = "1" },
|
|
27
|
+
} = process;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Make a GET request to the Griddo API.
|
|
31
|
+
*
|
|
32
|
+
* @template T Response Type returned.
|
|
33
|
+
* @returns {Promise<T>} A promise that is resolved with the data from the API response.
|
|
34
|
+
* @todo Maybe remove the loggin responsability
|
|
35
|
+
* @example
|
|
36
|
+
* const response = await get<Site>({
|
|
37
|
+
* endpoint: "...",
|
|
38
|
+
* body: {},
|
|
39
|
+
* cached: true,
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
async function getApi<T extends APIResponses>(props: GetAPI): Promise<T> {
|
|
43
|
+
const { endpoint, body, cached, attempt } = props;
|
|
44
|
+
const cacheOptions = { endpoint, body, cached };
|
|
45
|
+
|
|
46
|
+
// Start a timer
|
|
47
|
+
const start = new Date();
|
|
48
|
+
|
|
49
|
+
// Filesystem
|
|
50
|
+
if (cached) {
|
|
51
|
+
const cachedResponse = getCache<T>(cacheOptions);
|
|
52
|
+
|
|
53
|
+
if (cachedResponse) {
|
|
54
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
55
|
+
const siteId = getSafeSiteId(cachedResponse);
|
|
56
|
+
const siteIdMsg = siteId ? `site: ${siteId} ` : "";
|
|
57
|
+
|
|
58
|
+
logInfo(`GET (cache) ${siteIdMsg}${endpoint} - ${duration}s`);
|
|
59
|
+
|
|
60
|
+
return cachedResponse;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Network API
|
|
65
|
+
try {
|
|
66
|
+
// Success. Connection stablished.
|
|
67
|
+
const { data }: { data: T } = await axios({
|
|
68
|
+
url: endpoint,
|
|
69
|
+
method: "get",
|
|
70
|
+
headers: { ...AuthService.headers },
|
|
71
|
+
data: body,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
75
|
+
const siteId = getSafeSiteId(data);
|
|
76
|
+
const siteIdMsg = siteId ? `site: ${siteId} ` : "";
|
|
77
|
+
|
|
78
|
+
logInfo(`GET (fetch) ${siteIdMsg}${endpoint} - ${duration}s`);
|
|
79
|
+
|
|
80
|
+
// Save page object into filesystem
|
|
81
|
+
saveCache(cacheOptions, data);
|
|
82
|
+
|
|
83
|
+
// Return page object from API
|
|
84
|
+
return data;
|
|
85
|
+
} catch (e) {
|
|
86
|
+
// Failure. Network error
|
|
87
|
+
const error = e as Error;
|
|
88
|
+
showApiError(
|
|
89
|
+
error,
|
|
90
|
+
{ endpoint, body, attempt },
|
|
91
|
+
error.response.status !== 404
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if (error.response.status === 404) {
|
|
95
|
+
// @ts-expect-error
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Try again `RETRY_ATTEMPTS` times
|
|
100
|
+
if (attempt && attempt < parseInt(RETRY_ATTEMPTS)) {
|
|
101
|
+
console.warn("Waiting for retry: GET", endpoint);
|
|
102
|
+
await delay(parseInt(RETRY_WAIT_SECONDS) * 1000);
|
|
103
|
+
|
|
104
|
+
// Lets try again!
|
|
105
|
+
return getApi({ endpoint, body, cached, attempt: attempt + 1 });
|
|
106
|
+
} else {
|
|
107
|
+
throw new Error("Error in getApi()");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Make a PUT request to the Griddo API.
|
|
114
|
+
*
|
|
115
|
+
* @template T Response Type returned.
|
|
116
|
+
* @returns {Promise<T>} A promise that is resolved with the data from the API response.
|
|
117
|
+
*/
|
|
118
|
+
async function putApi<T extends APIResponses>(props: PutAPI): Promise<T> {
|
|
119
|
+
const { endpoint, body, cached = false, attempt = 0 } = props;
|
|
120
|
+
const cacheOptions = { endpoint, body, cached };
|
|
121
|
+
|
|
122
|
+
// Start a timer
|
|
123
|
+
const start = new Date();
|
|
124
|
+
|
|
125
|
+
// Filesystem
|
|
126
|
+
if (cached) {
|
|
127
|
+
const cachedResponse = getCache<T>(cacheOptions);
|
|
128
|
+
|
|
129
|
+
if (cachedResponse) {
|
|
130
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
131
|
+
|
|
132
|
+
logInfo(`PUT (cache) ${endpoint} - ${duration}s`);
|
|
133
|
+
|
|
134
|
+
return cachedResponse;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Network API
|
|
139
|
+
try {
|
|
140
|
+
const { data }: { data: T } = await axios({
|
|
141
|
+
url: endpoint,
|
|
142
|
+
method: "put",
|
|
143
|
+
headers: { ...AuthService.headers },
|
|
144
|
+
data: body,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
148
|
+
|
|
149
|
+
logInfo(`PUT (fetch) ${endpoint} - ${duration}s`);
|
|
150
|
+
|
|
151
|
+
// Save page object to filesystem
|
|
152
|
+
saveCache(cacheOptions, data);
|
|
153
|
+
|
|
154
|
+
// Return page object from API
|
|
155
|
+
return data;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
// Failure. Network error
|
|
158
|
+
const error = e as Error;
|
|
159
|
+
showApiError(
|
|
160
|
+
error,
|
|
161
|
+
{ endpoint, body, attempt },
|
|
162
|
+
error.response.status !== 404
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
if (error.response.status === 404) {
|
|
166
|
+
// @ts-expect-error
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Try again `RETRY_ATTEMPTS` times
|
|
171
|
+
if (attempt < parseInt(RETRY_ATTEMPTS)) {
|
|
172
|
+
console.warn("Waiting for retry: PUT", endpoint);
|
|
173
|
+
await delay(parseInt(RETRY_WAIT_SECONDS) * 1000);
|
|
174
|
+
|
|
175
|
+
// Lets try again!
|
|
176
|
+
return putApi({ endpoint, body, cached, attempt: attempt + 1 });
|
|
177
|
+
} else {
|
|
178
|
+
throw new Error("Error in putApi()");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Make a POST request to the Griddo API.
|
|
185
|
+
*
|
|
186
|
+
* @template T Response Type returned.
|
|
187
|
+
* @returns {Promise<T>} A promise that is resolved with the data from the API response.
|
|
188
|
+
*/
|
|
189
|
+
async function postApi<T extends APIResponses>(props: PostAPI): Promise<T> {
|
|
190
|
+
const { endpoint, body, headers, cached, attempt = 0 } = props;
|
|
191
|
+
|
|
192
|
+
const cacheOptions = { endpoint, body, headers, cached };
|
|
193
|
+
|
|
194
|
+
// Start a timer
|
|
195
|
+
const start = new Date();
|
|
196
|
+
const distributorBodyParams = endpoint.endsWith("/distributor")
|
|
197
|
+
? `# Distributor body: ${JSON.stringify(body)} - lang: ${JSON.stringify(
|
|
198
|
+
headers?.lang
|
|
199
|
+
)}`
|
|
200
|
+
: "";
|
|
201
|
+
|
|
202
|
+
// Filesystem
|
|
203
|
+
if (cached) {
|
|
204
|
+
const cachedResponse = getCache<T>(cacheOptions);
|
|
205
|
+
|
|
206
|
+
if (cachedResponse) {
|
|
207
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
208
|
+
|
|
209
|
+
logInfo(
|
|
210
|
+
`POST (cache) ${endpoint} - ${duration}s ${distributorBodyParams}`
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return cachedResponse;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Network API
|
|
218
|
+
try {
|
|
219
|
+
// Success. Connection stablished.
|
|
220
|
+
const { data }: { data: T } = await axios({
|
|
221
|
+
url: endpoint,
|
|
222
|
+
method: "post",
|
|
223
|
+
headers: { ...headers, ...AuthService.headers },
|
|
224
|
+
data: body,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const duration = msToSec(new Date().getTime() - start.getTime());
|
|
228
|
+
|
|
229
|
+
logInfo(`POST (fetch) ${endpoint} - ${duration}s`);
|
|
230
|
+
|
|
231
|
+
// Save page object to filesystem
|
|
232
|
+
saveCache(cacheOptions, data);
|
|
233
|
+
|
|
234
|
+
// Return page object from API
|
|
235
|
+
return data;
|
|
236
|
+
} catch (e) {
|
|
237
|
+
// Failure. Network error
|
|
238
|
+
const error = e as Error;
|
|
239
|
+
showApiError(
|
|
240
|
+
error,
|
|
241
|
+
{ endpoint, body, headers, attempt },
|
|
242
|
+
error.response.status !== 404
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
if (error.response.status === 404) {
|
|
246
|
+
// @ts-expect-error
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Try again `RETRY_ATTEMPTS` times
|
|
251
|
+
if (attempt < parseInt(RETRY_ATTEMPTS)) {
|
|
252
|
+
console.warn("Waiting for retry: POST", endpoint);
|
|
253
|
+
await delay(parseInt(RETRY_WAIT_SECONDS) * 1000);
|
|
254
|
+
|
|
255
|
+
// Lets try again!
|
|
256
|
+
return postApi({ endpoint, body, headers, cached, attempt: attempt + 1 });
|
|
257
|
+
} else {
|
|
258
|
+
throw new Error("Error in postApi()");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Shows an API error through the terminal.
|
|
265
|
+
*/
|
|
266
|
+
function showApiError(
|
|
267
|
+
error: Error,
|
|
268
|
+
callInfo: {
|
|
269
|
+
endpoint?: string;
|
|
270
|
+
// TODO: Remove any's
|
|
271
|
+
body?: any;
|
|
272
|
+
headers?: any;
|
|
273
|
+
attempt?: number;
|
|
274
|
+
} = {},
|
|
275
|
+
breakProcess = true
|
|
276
|
+
) {
|
|
277
|
+
const { response, message, stack } = error;
|
|
278
|
+
const { status, statusText, data } = response || {};
|
|
279
|
+
const callInfoArray = [];
|
|
280
|
+
|
|
281
|
+
for (const item of Object.keys(callInfo)) {
|
|
282
|
+
const itemCasted = item as keyof typeof callInfo;
|
|
283
|
+
callInfoArray.push(
|
|
284
|
+
`${item}: ${
|
|
285
|
+
typeof callInfo[itemCasted] === "object"
|
|
286
|
+
? JSON.stringify(callInfo[itemCasted])
|
|
287
|
+
: callInfo[itemCasted]
|
|
288
|
+
}`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Compose the errors output
|
|
293
|
+
const callInfoStr = callInfoArray.join("\n");
|
|
294
|
+
const apiResponseStr = response
|
|
295
|
+
? `Code: ${status} - ${statusText}\nResponse: ${JSON.stringify(data)}`
|
|
296
|
+
: "";
|
|
297
|
+
const errorDetailsStr = `${message}\n${stack}`;
|
|
298
|
+
|
|
299
|
+
// Print the error
|
|
300
|
+
console.warn(
|
|
301
|
+
chalk.bold.red(`
|
|
302
|
+
=============
|
|
303
|
+
|
|
304
|
+
{ Call info }
|
|
305
|
+
${callInfoStr}
|
|
306
|
+
|
|
307
|
+
{ API Response }
|
|
308
|
+
${apiResponseStr}
|
|
309
|
+
|
|
310
|
+
{ Error details }
|
|
311
|
+
${errorDetailsStr}
|
|
312
|
+
|
|
313
|
+
=============
|
|
314
|
+
`)
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
if (
|
|
318
|
+
breakProcess &&
|
|
319
|
+
(typeof callInfo?.attempt !== "number" ||
|
|
320
|
+
callInfo.attempt >= parseInt(RETRY_ATTEMPTS))
|
|
321
|
+
) {
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export { getApi as get, putApi as put, postApi as post };
|