![Český ráj (ceskyraj.com) scraper avatar](https://images.apifyusercontent.com/ssTRBdRQ-Y2xIsJKjruU9uwUIqhtYxKN6OCuZQ0UOKo/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS81S1dVNEpBLnBuZw.webp)
Český ráj (ceskyraj.com) scraper
DeprecatedView all Actors![Český ráj (ceskyraj.com) scraper](https://images.apifyusercontent.com/ssTRBdRQ-Y2xIsJKjruU9uwUIqhtYxKN6OCuZQ0UOKo/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS81S1dVNEpBLnBuZw.webp)
![](data:image/png;base64,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AAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGAQAQAAgEEEAAAABhEAAAAYRAAAAGDQ/wKAtyZK+HAxZwAAAABJRU5ErkJggg==)
This Actor is deprecated
This Actor is unavailable because the developer has decided to deprecate it. Would you like to try a similar Actor instead?
See alternative Actors![Český ráj (ceskyraj.com) scraper](https://images.apifyusercontent.com/ssTRBdRQ-Y2xIsJKjruU9uwUIqhtYxKN6OCuZQ0UOKo/rs:fill:92:92/aHR0cHM6Ly9pLmltZ3VyLmNvbS81S1dVNEpBLnBuZw.webp)
Český ráj (ceskyraj.com) scraper
strajk/cesky-raj-ceskyraj-com-scraper
Scrapes products titles, prices, images and availability. Does NOT scrape product details.
Dockerfile
1FROM apify/actor-node:16
2
3COPY package.json ./
4
5RUN npm --quiet set progress=false \
6 && npm install --only=prod --no-optional
7
8COPY . ./
INPUT_SCHEMA.json
1{
2 "title": "Český ráj (ceskyraj.com) scraper",
3 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details.",
4 "type": "object",
5 "schemaVersion": 1,
6 "properties": {
7 "mode": {
8 "title": "Mode",
9 "description": "",
10 "type": "string",
11 "editor": "select",
12 "default": "TEST",
13 "prefill": "TEST",
14 "enum": [
15 "TEST",
16 "FULL"
17 ],
18 "enumTitles": [
19 "TEST",
20 "FULL"
21 ]
22 },
23 "APIFY_DONT_STORE_IN_DATASET": {
24 "sectionCaption": "Advanced",
25 "sectionDescription": "Advanced options, use only if you know what you're doing.",
26 "title": "Don't store in dataset",
27 "description": "If set to true, the actor will not store the results in the default dataset. Useful when using alternative storage, like own database",
28 "type": "boolean",
29 "default": false,
30 "editor": "checkbox"
31 },
32 "PG_CONNECTION_STRING_NORMALIZED": {
33 "title": "Postgres connection string for normalized data",
34 "description": "If set, actor will store normalized data in Postgres database in PG_DATA_TABLE and PG_DATA_PRICE_TABLE tables",
35 "type": "string",
36 "editor": "textfield"
37 },
38 "PG_DATA_TABLE": {
39 "title": "Postgres table name for product data",
40 "description": "Table name for storing product name, url, image, ...",
41 "type": "string",
42 "editor": "textfield"
43 },
44 "PG_DATA_PRICE_TABLE": {
45 "title": "Postgres table name for price data",
46 "description": "Table name for storing price, original price, stock status, ...",
47 "type": "string",
48 "editor": "textfield"
49 }
50 },
51 "required": [
52 "mode"
53 ]
54}
apify.json
1{
2 "name": "cesky-raj-ceskyraj-com-scraper",
3 "version": "0.1",
4 "buildTag": "latest",
5 "env": null,
6 "defaultRunOptions": {
7 "build": "latest",
8 "timeoutSecs": 3600,
9 "memoryMbytes": 1024
10 }
11}
main.js
1import { URL } from "url";
2import { Actor } from "apify3";
3import { CheerioCrawler, createCheerioRouter } from "crawlee";
4import { init, save, toNumberOrNull } from "./_utils/common.js";
5
6var LABEL;
7
8(function (LABEL) {
9 LABEL["INDEX"] = "INDEX";
10 LABEL["PRODUCTS"] = "PRODUCTS";
11})(LABEL || (LABEL = {}));
12var MODE;
13
14(function (MODE) {
15 MODE["TEST"] = "TEST";
16 MODE["FULL"] = "FULL";
17})(MODE || (MODE = {}));
18
19async function enqueueInitial(type, crawler) {
20 if (type === MODE.FULL) {
21 await crawler.addRequests([
22 {
23 userData: { label: LABEL.INDEX },
24 url: `https://www.ceskyraj.com/nase-znacky/`,
25 },
26 ]);
27 } else if (type === MODE.TEST) {
28 const requests = [
29 {
30 userData: { label: LABEL.PRODUCTS },
31 url: `https://www.ceskyraj.com/hydrapak/`,
32 },
33 {
34 userData: { label: LABEL.PRODUCTS },
35 url: `https://www.ceskyraj.com/crankbrothers/`,
36 },
37 {
38 userData: { label: LABEL.PRODUCTS },
39 url: `https://www.ceskyraj.com/camelbak/`,
40 },
41 ];
42 await crawler.addRequests(requests);
43 }
44}
45
46const router = createCheerioRouter();
47
48router.addHandler(LABEL.INDEX, async ({ crawler, $ }) => {
49 const requests = [];
50 $(`#SubCategories ul.root li.leaf a.name`).each((i, el) => {
51 const url = $(el).attr(`href`);
52 const name = $(el).text();
53 requests.push({
54 url: `https://www.ceskyraj.com` + url,
55 userData: { label: LABEL.PRODUCTS, category: name },
56 });
57 });
58 await crawler.addRequests(requests);
59});
60
61router.addHandler(LABEL.PRODUCTS, async ({ crawler, $, request }) => {
62 console.log(`handleProducts`, request.url);
63 const PER_PAGE = 24;
64 if (!request.url.includes(`?f=`)) {
65 // on first page
66 const hasMore = !!$(`#CompoundPagingBottom a.nextProducts`).length;
67 const totalRaw = $(`#CompoundPagingBottom .displayedProducts`).text(); // e.g. `Zobrazeno 1-24 ze 52 produktů`
68 const total = parseInt(totalRaw.match(/ze (\d+) produktů/)[1]); // e.g. 52
69 let offset = PER_PAGE; // eg. 24, 48, 72, ...
70 while (offset < total) {
71 console.log(
72 `handleProducts`,
73 request.url,
74 `offset`,
75 offset,
76 `...enqueuing`
77 );
78 const paginatedUrl = new URL(request.url);
79 paginatedUrl.searchParams.set(`f`, offset.toString());
80 void crawler.addRequests([
81 {
82 userData: { label: LABEL.PRODUCTS },
83 url: paginatedUrl.toString(),
84 },
85 ]);
86 offset += PER_PAGE;
87 }
88 }
89
90 const products = [];
91 $(`#ProductsHost .ProductView`).each((i, el) => {
92 const id = $(el).attr(`id`);
93 const url = $(el).find(`h2 a`).attr(`href`);
94 const fullUrl = `https://www.ceskyraj.com` + url;
95 const title = $(el).find(`h2`).text();
96 const priceRaw = $(el).find(`.price.user`).text(); // `640 Kč`
97 const price = priceRaw.replace(/\D/g, ``);
98 const priceOrigRaw = $(el).find(`.price.retail`).text(); // `640 Kč`
99 const priceOrig = priceOrigRaw.replace(/\D/g, ``);
100 const img = $(el).find(`.crImages img.thumbnail`).attr(`data-src`);
101 const inStock = $(el).find(`.AvailabilityView`).text().includes(`skladem`);
102
103 const product = {
104 pid: id,
105 name: title,
106 url: fullUrl,
107 img: img,
108 inStock,
109 currentPrice: toNumberOrNull(price),
110 originalPrice: toNumberOrNull(priceOrig),
111 currency: `CZK`,
112 };
113 products.push(product);
114 });
115 await save(products);
116});
117
118void Actor.main(async () => {
119 const input = await Actor.getInput();
120 const { mode = MODE.FULL, ...rest } = input ?? {};
121 await init({ actorNameOverride: `ceskyraj-com` }, rest);
122 const crawler = new CheerioCrawler({ requestHandler: router });
123 await enqueueInitial(mode, crawler);
124 await crawler.run();
125});
package.json
1{
2 "name": "cesky-raj-ceskyraj-com-scraper",
3 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details.",
4 "type": "module",
5 "scripts": {
6 "start": "node ./main.js",
7 "push-to-apify-platform": "npx apify push"
8 },
9 "dependencies": {
10 "apify3": "npm:apify@^3.0.2",
11 "crawlee": "*",
12 "pg": "*",
13 "pg-connection-string": "*",
14 "dotenv": "*",
15 "find-config": "*",
16 "@elastic/elasticsearch": "*",
17 "filenamify": "*"
18 },
19 "apify": {
20 "title": "Český ráj (ceskyraj.com) scraper",
21 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details.",
22 "isPublic": true,
23 "isDeprecated": false,
24 "isAnonymouslyRunnable": true,
25 "notice": "",
26 "pictureUrl": "",
27 "seoTitle": "",
28 "seoDescription": "",
29 "categories": [
30 "ECOMMERCE"
31 ]
32 }
33}
.actor/actor.json
1{
2 "actorSpecification": 1,
3 "name": "cesky-raj-ceskyraj-com-scraper",
4 "title": "Český ráj (ceskyraj.com) scraper",
5 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details.",
6 "version": "0.1.0",
7 "storages": {
8 "dataset": {
9 "actorSpecification": 1,
10 "title": "Český ráj (ceskyraj.com) scraper",
11 "description": "Scrapes products titles, prices, images and availability. Does NOT scrape product details.",
12 "views": {
13 "overview": {
14 "title": "Overview",
15 "description": "Overview of the most important fields",
16 "transformation": {
17 "fields": [
18 "pid",
19 "name",
20 "url",
21 "img",
22 "inStock",
23 "currentPrice",
24 "originalPrice",
25 "currency"
26 ]
27 },
28 "display": {
29 "component": "table",
30 "columns": [
31 {
32 "label": "Pid",
33 "field": "pid",
34 "format": "text"
35 },
36 {
37 "label": "Name",
38 "field": "name",
39 "format": "text"
40 },
41 {
42 "label": "Url",
43 "field": "url",
44 "format": "link"
45 },
46 {
47 "label": "Img",
48 "field": "img",
49 "format": "image"
50 },
51 {
52 "label": "In Stock",
53 "field": "inStock",
54 "format": "boolean"
55 },
56 {
57 "label": "Current Price",
58 "field": "currentPrice",
59 "format": "number"
60 },
61 {
62 "label": "Original Price",
63 "field": "originalPrice",
64 "format": "number"
65 },
66 {
67 "label": "Currency",
68 "field": "currency",
69 "format": "text"
70 }
71 ]
72 }
73 }
74 }
75 }
76 }
77}
.actor/logo.png
_utils/common.js
1import { createHash } from 'crypto'
2import os from "os"
3import path from "path"
4// eslint-disable-next-line @apify/apify-actor/no-forbidden-node-internals
5import fs from "fs"
6import pg from "pg"
7import pgConnectionString from 'pg-connection-string'
8import { config } from 'dotenv'
9import findConfig from "find-config"
10import { Client as ElasticClient } from "@elastic/elasticsearch"
11import filenamify from 'filenamify'
12import { Dataset } from 'crawlee'
13
14config({ path: findConfig(`.env`) })
15
16const elasticIndexName = `actors-monorepo-shops`
17
18const globalLogsProps = {
19 __NODE_STARTED: new Date().toISOString(),
20}
21
22let actorName
23let pgClient
24let pgClientNormalized
25let elasticClient
26export async function init ({ actorNameOverride }, restInput) {
27 parseEnvFromInput(restInput)
28
29 if (os.platform() === `darwin`) {
30 const filePath = process.argv[1] // ~/Projects/apify-actors-monorepo/actors/foo.ts
31 const basename = path.basename(filePath) // foo.ts
32 actorName = actorNameOverride ?? basename.split(`.`)[0] // foo
33 const gitBranch = fs.readFileSync(path.join(process.cwd(), `..`, `.git/HEAD`), `utf8`)
34 .split(` `)[1]
35 .trim()
36 .replace(`refs/heads/`, ``)
37 const gitCommit = fs.readFileSync(path.join(process.cwd(), `..`, `.git/refs/heads/${gitBranch}`), `utf8`)
38 const gitCommitShort = gitCommit.substring(0, 7)
39 globalLogsProps.__GIT_COMMIT = gitCommitShort
40 }
41
42 if (process.env.APIFY_IS_AT_HOME) {
43 actorName = actorNameOverride ?? process.env.APIFY_ACTOR_ID // Name would be better, but it's not in ENV
44 }
45
46 /* ELASTIC */
47 /* ======= */
48 if (process.env.ELASTIC_CLOUD_ID) {
49 elasticClient = new ElasticClient({
50 cloud: { id: process.env.ELASTIC_CLOUD_ID },
51 auth: { apiKey: process.env.ELASTIC_CLOUD_API_KEY },
52 })
53
54 // const mapping = await elasticClient.indices.getMapping({ index: actorName })
55
56 // eslint-disable-next-line no-inner-declarations
57 async function enforceIndexMapping () {
58 const doesIndexExist = await elasticClient.indices.exists({ index: elasticIndexName })
59 if (!doesIndexExist) await elasticClient.indices.create({ index: elasticIndexName })
60 await elasticClient.indices.putMapping({
61 index: elasticIndexName,
62 body: {
63 properties: {
64 _discount: { type: `float` },
65 originalPrice: { type: `float` },
66 currentPrice: { type: `float` },
67 },
68 },
69 })
70 }
71
72 try {
73 await enforceIndexMapping()
74 } catch (err) {
75 if (err.message.includes(`cannot be changed from type`)) {
76 console.log(`Elastic index ${elasticIndexName} already exists with incorrect mappings. As existing mapping cannot be changed, index will be deleted and recreated.`)
77 await elasticClient.indices.delete({ index: elasticIndexName })
78 await enforceIndexMapping()
79 }
80 }
81 }
82
83 /* POSTGRESQL */
84 /* ========== */
85 if (process.env.PG_CONNECTION_STRING) {
86 const pgConfig = pgConnectionString(process.env.PG_CONNECTION_STRING)
87 // const pgPool = new pg.Pool(pgConfig)
88
89 pgClient = new pg.Client(pgConfig)
90 await pgClient.connect()
91
92 // Check if table exists and have proper columns
93 const { rows: tables } = await pgClient.query(`
94 SELECT table_name
95 FROM information_schema.tables
96 WHERE table_schema = 'public'
97 `)
98
99 // eslint-disable-next-line camelcase
100 const tableExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_TABLE)
101 if (!tableExists) {
102 throw new Error(`Table ${process.env.PG_DATA_TABLE} does not exist in database ${pgConfig.database}`)
103 }
104
105 // TODO: Handle pgClient closing
106 }
107
108 if (process.env.PG_CONNECTION_STRING_NORMALIZED) {
109 const pgConfig = pgConnectionString(process.env.PG_CONNECTION_STRING_NORMALIZED)
110
111 pgClientNormalized = new pg.Client(pgConfig)
112 await pgClientNormalized.connect()
113
114 // Check if table exists and have proper columns
115 const { rows: tables } = await pgClientNormalized.query(`
116 SELECT table_name
117 FROM information_schema.tables
118 WHERE table_schema = 'public'
119 `)
120
121 // eslint-disable-next-line camelcase
122 const tableMainExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_TABLE)
123 // eslint-disable-next-line camelcase
124 const tablePricesExists = tables.some(({ table_name }) => table_name === process.env.PG_DATA_PRICE_TABLE)
125 if (!tableMainExists) throw new Error(`Table ${process.env.PG_DATA_TABLE} does not exist in database ${pgConfig.database}`)
126 if (!tablePricesExists) throw new Error(`Table ${process.env.PG_DATA_PRICE_TABLE} does not exist in database ${pgConfig.database}`)
127
128 // TODO: Handle pgClient closing
129 }
130}
131
132// inspired by @drobnikj
133// TODO: Similar, but less obfuscated for easier debugging
134export const createUniqueKeyFromUrl = (url) => {
135 const hash = createHash(`sha256`)
136 const cleanUrl = url.split(`://`)[1] // Remove protocol
137 hash.update(cleanUrl)
138 return hash.digest(`hex`)
139}
140
141/**
142 *
143 * @param {Date} datetime
144 * @return {Promise<void>}
145 */
146export const sleepUntil = async (datetime) => {
147 const now = new Date()
148 const difference = datetime - now
149 if (difference > 0) {
150 return new Promise((resolve) => {
151 setTimeout(resolve, difference)
152 })
153 }
154 return Promise.resolve()
155}
156
157export function parsePrice (string) {
158 let amount, currency
159 const noText = string.replace(/[^\d,.]/g, ``)
160 const decimals = noText.match(/([,.])(\d{2})$/)
161 if (decimals) {
162 const decimalSeparator = decimals[1]
163 // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
164 const decimalAmount = decimals[2]
165 amount = parseInt(noText.split(decimalSeparator)[0])
166 } {
167 const justNumbers = noText.replace(/[,.]/g, ``)
168 amount = parseInt(justNumbers)
169 }
170 return { amount, currency }
171}
172
173export function toNumberOrNull (str) {
174 // TODO: Handle better, but only after adding test
175 if (str === undefined) return null
176 if (str === null) return null
177 if (str === ``) return null
178 const num = Number(str)
179 if (Number.isNaN(num)) return null
180 return num
181}
182
183export async function save (objs) {
184 if (!Array.isArray(objs)) objs = [objs]
185 if (objs.length === 0) return
186
187 const objsExtended = objs.map((obj) => {
188 const objExtended = {
189 ...obj,
190 actorName,
191 ...globalLogsProps,
192 // __NODE_VERSION: global.process.versions.node,
193 // __NODE_UPTIME: global.process.uptime().toFixed(2), // seconds, 2 decimals
194 }
195 // if run on Apify
196 if (process.env.APIFY_IS_AT_HOME) {
197 objExtended.__APIFY_ACTOR_ID = process.env.APIFY_ACTOR_ID
198 objExtended.__APIFY_ACTOR_RUN_ID = process.env.APIFY_ACTOR_RUN_ID
199 objExtended.__APIFY_ACTOR_BUILD_ID = process.env.APIFY_ACTOR_BUILD_ID
200 objExtended.__APIFY_ACTOR_BUILD_NUMBER = process.env.APIFY_ACTOR_BUILD_NUMBER
201 objExtended.__APIFY_ACTOR_TASK_ID = process.env.APIFY_ACTOR_TASK_ID
202 if (!process.env.APIFY_DONT_STORE_IN_DATASET) void Dataset.pushData(obj)
203 }
204 return objExtended
205 })
206 // if runs on local machine (MacOS)
207 if (os.platform() === `darwin`) {
208 const cwd = process.cwd() // ~/Projects/apify-actors-monorepo/actors
209 const storageDir = path.join(cwd, `${actorName}.storage`) // ~/Projects/apify-actors-monorepo/actors/foo.storage
210 if (!fs.existsSync(storageDir)) fs.mkdirSync(storageDir)
211 const dataDir = path.join(storageDir, `data`) // ~/Projects/apify-actors-monorepo/actors/foo.storage/data
212 if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir)
213 for (const objExtended of objsExtended) {
214 const id = objExtended.id ?? objExtended.pid // ?? uuidv4()
215 const fileName = `${filenamify(id)}.json`
216 const dataFilePath = path.join(dataDir, fileName) // ~/Projects/apify-actors-monorepo/actors/foo.storage/data/foo.json
217 fs.writeFileSync(dataFilePath, JSON.stringify(objExtended, null, 2))
218 }
219 }
220
221 if (pgClient) {
222 const objsPg = objs.map((obj) => ({
223 ...obj,
224 // TODO: This is becoming not nice, and not clear
225 shop: actorName,
226 scrapedAt: new Date().toISOString().split(`T`)[0],
227 }))
228
229 const columns = getColumns(objsPg)
230 const values = getValues(objsPg)
231 const queryString = `
232 INSERT INTO public."${process.env.PG_DATA_TABLE}" (${columns})
233 VALUES (${values})
234 `
235 try {
236 const { rowCount } = await pgClient.query(queryString)
237 console.log(`[save] saved to database: ${JSON.stringify(rowCount)}`)
238 } catch (err) {
239 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
240 else throw err
241 }
242 }
243
244 // Only make sense for HlidacShopu
245 if (pgClientNormalized) {
246 const objsPgData = objs.map((obj) => ({
247 shop: actorName,
248 pid: obj.pid,
249 name: obj.name,
250 url: obj.url,
251 img: obj.img,
252 }))
253
254 const objsPgDataPrice = objs.map((obj) => ({
255 shop: actorName,
256 pid: obj.pid,
257 scrapedAt: new Date().toISOString().split(`T`)[0],
258 currentPrice: obj.currentPrice,
259 originalPrice: obj.originalPrice,
260 inStock: obj.inStock,
261 }))
262
263 const queryString = `
264 INSERT INTO public."${process.env.PG_DATA_TABLE}" (${getColumns(objsPgData)})
265 VALUES (${getValues(objsPgData)})
266 ON CONFLICT DO NOTHING
267 `
268 try {
269 const { rowCount } = await pgClientNormalized.query(queryString)
270 console.log(`[save] saved to database (data): ${JSON.stringify(rowCount)}`)
271 } catch (err) {
272 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
273 else throw err
274 }
275
276 const queryStringPrice = `
277 INSERT INTO public."${process.env.PG_DATA_PRICE_TABLE}" (${getColumns(objsPgDataPrice)})
278 VALUES (${getValues(objsPgDataPrice)})
279 ON CONFLICT DO NOTHING
280 `
281 try {
282 const { rowCount } = await pgClientNormalized.query(queryStringPrice)
283 console.log(`[save] saved to database (price): ${JSON.stringify(rowCount)}`)
284 } catch (err) {
285 if (err.message.includes(`violates unique constraint`)) console.warn(`PostgresSQL: violates unique constraint`)
286 else throw err
287 }
288 }
289
290 if (elasticClient) {
291 // .index creates or updates the document
292 // .create creates a new document if it doesn't exist, 409 if it does
293 // try {
294 // const res = await elasticClient.index({
295 // index: `actors-monorepo-shops`, // TODO: Consider using actorName
296 // id, // foo-bar
297 // document: objExtended, // {...}
298 // })
299 // } catch (err) {
300 // // https://discuss.elastic.co/t/elasticsearch-503-ok-false-message-the-requested-deployment-is-currently-unavailable/200583
301 // if (err.message.includes(`requested resource is currently unavailable`)) console.log(`Elasticsearch is unavailable, skipping, but not aborting`)
302 // else throw err
303 // }
304 }
305}
306
307function getColumns (objs) {
308 return Object.keys(objs[0]).map((key) => `"${key}"`).join(`, `)
309}
310
311function getValues (objs) {
312 return objs.map(objPg => Object.values(objPg).map((value) => {
313 // escape strings to prevent SQL injection
314 if (typeof value === `string`) return `'${value.replace(/'/g, `''`)}'`
315 // convert to DB specific null
316 if (typeof value === `undefined` || value === null) return `NULL`
317 return value
318 }).join(`, `)).join(`), (`)
319}
320
321export function parseEnvFromInput (input) {
322 const env = {}
323 for (const key in input) {
324 if (key === key.toUpperCase()) env[key] = input[key]
325 }
326 console.log(`[parseEnvFromInput] ${JSON.stringify(env)}`)
327 Object.assign(process.env, env)
328}
Developer
Maintained by Community
Categories