UNPKG

4.19 kBPlain TextView Raw
1import { Source, SourceCallback, SourceOptions } from '@neoskop/paperboy';
2
3import { connect, Message } from 'amqplib';
4
5import { fetchDamAssets } from './dam.util';
6import { MagnoliaSourceOptions } from './magnolia-source-options.interface';
7import {
8 fetchPages, fetchSitemap, fetchWorkspace, sanitizeJson, writePagesFile, writeWorkspaceFile
9} from './pages.util';
10import AsyncLock = require("async-lock");
11
12export class MagnoliaSource implements Source {
13 private readonly options: MagnoliaSourceOptions;
14 private readonly callback: SourceCallback;
15 private readonly generationLock: AsyncLock = new AsyncLock({maxPending: 1});
16
17 constructor(options: SourceOptions, callback: SourceCallback) {
18 this.options = <MagnoliaSourceOptions>options;
19 this.callback = callback;
20 }
21
22 public generate(): Promise<void> {
23 return new Promise(async resolve => {
24 const sitemap = await fetchSitemap(this.options);
25 const website = await fetchPages(this.options);
26 const pages = sitemap
27 .map(path => website.find((page: any) => page['@path'] === path))
28 .filter(page => typeof page !== 'undefined');
29
30 const workspaces: { [workspace: string]: any } = {};
31
32 for (const workspace of this.options.magnolia.workspaces) {
33 workspaces[workspace] = await fetchWorkspace(workspace, this.options);
34 }
35
36 // get dam jcr ids
37 const nodes = pages.concat(
38 Object.keys(workspaces).reduce((prev, current) => prev.concat(workspaces[current]), [])
39 );
40 const match = JSON.stringify(nodes).match(
41 /jcr:([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/g
42 );
43
44 let damUuids = match ? match.map(id => id.substring(4)) : [];
45 damUuids = damUuids.filter((id, pos) => {
46 return damUuids.indexOf(id) === pos;
47 });
48
49 const damAssets = await fetchDamAssets(damUuids, this.options);
50 const pagesObj: any = pages.map((page: any) =>
51 sanitizeJson(page, damAssets, pages, this.options)
52 );
53
54 await writePagesFile(pagesObj, this.options);
55
56 if (this.options.magnolia.workspaces) {
57 for (const workspace of Object.keys(workspaces)) {
58 const workspaceData = workspaces[workspace];
59
60 if (workspaceData) {
61 const sanitized: any[] = [];
62
63 for (const item of workspaceData) {
64 sanitized.push(sanitizeJson(item, damAssets, workspaceData, this.options));
65 }
66
67 await writeWorkspaceFile(workspace, sanitized, this.options);
68 }
69 }
70 }
71
72 resolve();
73 });
74 }
75
76 public async start(): Promise<void> {
77 connect(this.options.queue.uri)
78 .then(conn => {
79 conn.on('error', this.retryConnection.bind(this, this.options.queue));
80 return conn.createChannel();
81 })
82 .then(channel => {
83 channel
84 .assertExchange(this.options.queue.exchangeName || 'paperboy', 'fanout', {
85 durable: false
86 })
87 .then(() => {
88 return channel.assertQueue(null, {
89 autoDelete: true
90 });
91 })
92 .then(qok => {
93 channel.bindQueue(qok.queue, this.options.queue.exchangeName || 'paperboy', '');
94 channel.consume(qok.queue, this.consumeMessage.bind(this), {
95 noAck: true
96 });
97 });
98 })
99 .catch(() => {
100 this.retryConnection();
101 });
102 }
103
104 private retryConnection() {
105 console.info('Connection to queue failed, will retry in 10s...');
106 setTimeout(() => {
107 this.start();
108 }, 10000);
109 }
110
111 private consumeMessage(message: Message | null) {
112 console.info(
113 "[x] from Magnolia: %s -> '%s'",
114 message.fields.routingKey,
115 message.content.toString()
116 );
117
118 this.generationLock.acquire('generationLock', (done) => {
119 this.generate().then(() => {
120 this.callback().then(() => done());
121 }).catch((err) => {
122 console.error('Generation failed.', err);
123 done()
124 });
125 }, (err, ret) => {
126 if (err) {
127 console.info('Already another pending message. Message discarded!');
128 }
129 });
130 }
131}