Skip to content

Commit

Permalink
Merge pull request #11 from openearth/sync-viewer-layer
Browse files Browse the repository at this point in the history
Sync viewer layer
  • Loading branch information
sjoerdbeentjes authored Aug 2, 2024
2 parents 9e52699 + 66a410c commit 93f2f0e
Show file tree
Hide file tree
Showing 6 changed files with 480 additions and 4 deletions.
246 changes: 246 additions & 0 deletions src/api/sync-viewer-layer-background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
import { Geonetwork } from '../lib/geonetwork'
import { datocmsRequest } from '../lib/datocms'
import { addThumbnailsToRecord } from '../lib/add-thumbnails-to-record'
import { withServerDefaults } from '../lib/with-server-defaults'
import { buildMenuTree } from '../lib/build-menu-tree'
import { findGeonetworkInstances } from '../lib/find-geonetwork-instances'
import { fetchViewerLayerXML } from '../lib/fetch-viewer-layer-xml'
import { formatMenusRecursive } from '../lib/format-menu'
import Mailjet from 'node-mailjet'
import fs from 'fs';

const mailjet = new Mailjet({
apiKey: process.env.MAILJET_API_TOKEN,
apiSecret: process.env.MAILJET_API_SECRET,
})

const viewersWithViewerLayersQuery = /* graphql */ `
query viewersWithViewerLayersQuery ($first: IntType, $skip: IntType = 0, $locale: SiteLocale = nl) {
menus: allMenus(first: $first, skip: $skip, locale: $locale) {
id
geonetwork {
baseUrl
username
password
}
errorNotificationContacts {
email
}
children: viewerLayers {
id
}
parent {
id
}
}
_allMenusMeta {
count
}
}`

const viewerLayerByIdQuery = /* graphql */ `
query LayerById($id: ItemId) {
viewerLayer(filter: {id: {eq: $id}}) {
layer {
thumbnails {
filename
url
}
}
}
}`

export const handler = withServerDefaults(async (event, _) => {
/* Protect this endpoint by using a token */
if (process.env.SYNC_LAYER_API_TOKEN !== event.headers['x-api-key']) {
return {
statusCode: 401,
}
}

const data = JSON.parse(event.body)

const id = data.entity.id

const { menus } = await datocmsRequest({
query: viewersWithViewerLayersQuery,
preview: true
})
const formattedMenus = formatMenusRecursive(menus)
const menuTree = buildMenuTree(formattedMenus)

try {
const type = data.related_entities.find(entity => entity.type === 'item_type').attributes.api_key

if (type === 'viewer_layer') {
await syncViewerLayers(menuTree, data.event_type, id)
} else if (type === 'menu') {
await syncViewer(menuTree, data.event_type, id)
}
}
catch (e) {
console.log('The following error occured', e.message)

for (let email of findEmailContactsForLayerId(menuTree, layerId)) {
console.log('Sending email to', email)

await mailjet.post('send', { version: 'v3.1' }).request({
Messages: [
{
From: {
Email: process.env.MAILJET_FROM_EMAIL,
},
To: [
{
Email: email,
},
],
Subject: `Fout bij opslaan metadata voor laag ${layerId}`,
HTMLPart: e.message,
},
],
})
}
}
})

async function syncViewer(menuTree, eventType, viewerId) {
const viewerLayers = new Set()

const findChilrenInMenu = (menu, viewerId) => {
const { children } = menu

if (children) {
children.forEach((child) => {
if (child.id === viewerId && child.children) {
child.children.forEach((viewerLayer) => {
viewerLayers.add(viewerLayer.id)
})
}

findChilrenInMenu(child, viewerId)
})
}
}

menuTree.forEach((viewer) => {
findChilrenInMenu(viewer, viewerId)
})

const viewerLayersArray = Array.from(viewerLayers)


const requestsPromises = viewerLayersArray.map(
async (viewerLayerId) => {
await syncViewerLayers(menuTree, eventType, viewerLayerId)
}
)

const results = await Promise.allSettled(requestsPromises)
}

async function syncViewerLayers(menuTree, eventType, viewerLayerId) {
const geonetworkInstances = findGeonetworkInstances(menuTree, viewerLayerId)

const geonetworkInstancesArray = Array.from(geonetworkInstances)

const xml = await fetchViewerLayerXML({ id: viewerLayerId })

// Can occur when no update needs to be done (because there is no factsheet or inspireMetadata)
if (xml === null) {
return
}

const requestsPromises = geonetworkInstancesArray.map(
async ([_, geonetworkInstance]) => {
const { baseUrl, username, password } = geonetworkInstance

const geonetwork = new Geonetwork(
baseUrl + 'geonetwork/srv/api',
username,
password
)

switch (eventType) {
case 'create': {
await geonetwork.recordsRequest({
url: '?publishToAll=true',
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: xml,
})

break
}

case 'publish': {
await geonetwork.recordsRequest({
url: '?uuidProcessing=OVERWRITE&publishToAll=true',
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: xml,
})

break
}
}

switch (eventType) {
case 'create':
case 'publish':
const { viewerLayer } = await datocmsRequest({
query: viewerLayerByIdQuery,
variables: { id: viewerLayerId },
})

await addThumbnailsToRecord(viewerLayer?.layer?.thumbnails, viewerLayerId, geonetwork)
}
}
)

const results = await Promise.allSettled(requestsPromises)

const errors = results.filter(result => result.status === 'rejected')

if (errors.length) {
const errorMessage = `<ul>${errors.map(error => `<li>${error.reason}</li>`).join('')}</ul>`
throw new Error(errorMessage)
}

}

function findEmailContactsForLayerId(menuTree, layerId) {
const contacts = new Set()

menuTree.forEach((viewer) => {

const findInMenu = (menu) => {
const { children } = menu

if (children) {
children.forEach((child) => {

if (child.layer.id === layerId) {
const { errorNotificationContacts } = viewer

if (errorNotificationContacts.length) {
for (let { email } of errorNotificationContacts) {
contacts.add(email)
}
}
}

findInMenu(child)
})
}
}

findInMenu(viewer)
})

return contacts
}
49 changes: 49 additions & 0 deletions src/api/viewer-layer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import convert from 'xml-js'
import { withServerDefaults } from '../lib/with-server-defaults'
import { contentTypes } from '../lib/constants'
import { fetchViewerLayerXML } from '../lib/fetch-viewer-layer-xml'

export const handler = withServerDefaults(async (event, _) => {
const { id, format } = event.queryStringParameters

if (!id) {
return {
statusCode: 404,
body: JSON.stringify({ error: 'id query parameter is required' }),
}
}

if (!format) {
return {
statusCode: 404,
body: JSON.stringify({ error: 'format query parameter is required' }),
}
}

if (!['json', 'xml'].includes(format)) {
return {
statusCode: 400,
body: JSON.stringify({
error:
'format query parameter must be one of the following values: json|xml',
}),
}
}

let formatted = await fetchViewerLayerXML({
id,
})

if (format === 'json') {
formatted = convert.xml2json(formatted, {
compact: true,
})
}

return {
body: formatted,
headers: {
'content-type': contentTypes[format],
},
}
})
1 change: 1 addition & 0 deletions src/lib/datocms.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function executeFetch(query, variables = {}, preview = false) {
headers: {
'Content-Type': 'application/json',
Authorization: process.env.DATO_API_TOKEN,
'X-Environment': 'production',
},
body: JSON.stringify({ query, variables }),
})
Expand Down
2 changes: 0 additions & 2 deletions src/lib/fetch-layer-xml.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,6 @@ export async function fetchLayerXML({ id }) {
...viewerLayer
} } = await datocmsRequest({ query, variables: { id } })

console.log(layer)

const data = {
layer: {
...layer,
Expand Down
Loading

0 comments on commit 93f2f0e

Please sign in to comment.