@@ -10,8 +10,10 @@ import fetch from 'isomorphic-fetch';
10
10
// import logger from 'utils/logger';
11
11
// import moment from 'moment';
12
12
import qs from 'qs' ;
13
+ import { logger } from 'topcoder-react-lib' ;
13
14
14
15
const contentful = require ( 'contentful-management' ) ;
16
+ const xml2json = require ( 'xml2json' ) ;
15
17
16
18
/* Holds Contentful CDN URL. */
17
19
const CDN_URL = 'https://cdn.contentful.com/spaces' ;
@@ -191,6 +193,125 @@ export function articleVote(body) {
191
193
. then ( entry => entry . publish ( ) ) ;
192
194
}
193
195
196
+ /**
197
+ * This function fetches TC RSS feed to create draft articles in THRIVE for the new posted blogs.
198
+ * It calls itself by interval to poll for new blogs.
199
+ * Runs on server side only.
200
+ */
201
+ export async function pollArticlesForThrive ( ) {
202
+ // make this function execute only when in production
203
+ if ( process . env . BABEL_ENV !== 'production' ) return ;
204
+ logger . log ( 'polling blog articles for THRIVE -> INIT' ) ;
205
+ // Create Contentful client to work with
206
+ const client = contentful . createClient ( {
207
+ accessToken : config . SECRET . CONTENTFUL . MANAGEMENT_TOKEN ,
208
+ } ) ;
209
+ // connect to Thrive space
210
+ const env = await client . getSpace ( config . SECRET . CONTENTFUL . EDU . SPACE_ID )
211
+ . then ( space => space . getEnvironment ( 'master' ) ) ;
212
+ // fetch the poll interval
213
+ const pollConf = await env . getEntry ( '7n1HT3MhUhgjvCzd36Nymd' ) ;
214
+ const pollInt = pollConf ? pollConf . fields . timeInSeconds : 86400 ; // default to day if not found
215
+ // fetch the RSS feed and parse it
216
+ const feedXML = await fetch ( config . URL . THRIVE_POLL_FEED ) ;
217
+ if ( feedXML . ok ) {
218
+ let feed = await feedXML . text ( ) ;
219
+ feed = await Promise . resolve ( xml2json . toJson ( feed , { object : true } ) ) ;
220
+ if ( feed ) {
221
+ // when feed loaded and parsed ok
222
+ // apply filter first based on tags work only with 'Community Stories'
223
+ feed . rss . channel . item = _ . filter (
224
+ feed . rss . channel . item ,
225
+ blogItem => blogItem . category . indexOf ( 'Community Stories' ) !== - 1 ,
226
+ ) ;
227
+ // loop all feed items and check if those exists in space
228
+ // if not existing then create
229
+ Promise . all (
230
+ _ . map ( feed . rss . channel . item ,
231
+ blogPost => env . getEntries ( {
232
+ content_type : 'article' ,
233
+ 'fields.title[match]' : blogPost . title ,
234
+ } )
235
+ . then ( ( queryData ) => {
236
+ if ( queryData . total === 0 ) {
237
+ // article not found in Contentful space
238
+ // will create it with payload...
239
+ const article = {
240
+ fields : {
241
+ title : { 'en-US' : blogPost . title } ,
242
+ type : { 'en-US' : 'Article' } ,
243
+ tags : { 'en-US' : blogPost . category } ,
244
+ creationDate : { 'en-US' : new Date ( blogPost . pubDate ) } ,
245
+ content : { 'en-US' : blogPost . description } ,
246
+ externalArticle : { 'en-US' : true } ,
247
+ contentUrl : { 'en-US' : blogPost . link } ,
248
+ } ,
249
+ } ;
250
+ // check if author exists
251
+ // if yes link it if no create it?
252
+ return env . getEntries ( {
253
+ content_type : 'person' ,
254
+ query : blogPost [ 'dc:creator' ] ,
255
+ } )
256
+ . then ( ( author ) => {
257
+ // found an author that matches link it
258
+ if ( author . total ) {
259
+ article . fields . contentAuthor = {
260
+ 'en-US' : [ {
261
+ sys : {
262
+ type : 'Link' , linkType : 'Entry' , id : author . items [ 0 ] . sys . id ,
263
+ } ,
264
+ } ] ,
265
+ } ;
266
+ }
267
+ // try get the page to extract its featured image
268
+ return fetch ( blogPost . link )
269
+ . then ( rsp => rsp . text ( ) )
270
+ . then ( ( HTMLsrc ) => {
271
+ const match = / < i m a g e [ ^ > ] * h r e f = " ( [ ^ " ] + ) " / gm. exec ( HTMLsrc ) ;
272
+ if ( match && match [ 1 ] ) {
273
+ // create the asset in Contentful
274
+ return env . createAsset ( {
275
+ fields : {
276
+ title : { 'en-US' : blogPost . title } ,
277
+ file : {
278
+ 'en-US' : { fileName : blogPost . title , contentType : 'image' , upload : match [ 1 ] } ,
279
+ } ,
280
+ } ,
281
+ } )
282
+ . then ( asset => asset . processForAllLocales ( ) )
283
+ . then ( ( asset ) => {
284
+ article . fields . featuredImage = {
285
+ 'en-US' : {
286
+ sys : {
287
+ type : 'Link' , linkType : 'Asset' , id : asset . sys . id ,
288
+ } ,
289
+ } ,
290
+ } ;
291
+ return env . createEntry ( 'article' , article ) ;
292
+ } ) ;
293
+ }
294
+ // could not find image
295
+ // just create the article without it...
296
+ return env . createEntry ( 'article' , article ) ;
297
+ } ) ;
298
+ } ) ;
299
+ }
300
+ // Article exists in Contentful space
301
+ // do nothing...
302
+ return 1 ;
303
+ } ) ) ,
304
+ )
305
+ . then ( ( ) => logger . log ( 'polling blog articles for THRIVE -> DONE' ) ) ;
306
+ }
307
+ }
308
+ // schedule a repeat each TC_EDU_POLL_TIME
309
+ setTimeout (
310
+ pollArticlesForThrive ,
311
+ pollInt * 1000 + ( Math . floor ( Math . random ( ) * Math . floor ( 5 ) ) * 60 * 1000 ) ,
312
+ ) . unref ( ) ;
313
+ }
314
+
194
315
// /* Contentful CDN service. */
195
316
// export const cdnService = new ApiService(CDN_URL, CDN_KEY);
196
317
0 commit comments