@oraclecc/dcu
Version:
Development tools for Oracle Commerce Cloud.
251 lines (201 loc) • 9.2 kB
JavaScript
const Promise = require("bluebird")
const constants = require("./constants").constants
const endPointTransceiver = require("./endPointTransceiver")
const exists = require("./utils").exists
const getWidgetInstancePath = require("./widgetInstanceGrabber").getWidgetInstancePath
const getGrabbingConcurrency = require("./concurrencySettings").getGrabbingConcurrency
const getDirectoryForWidget = require("./widgetGrabber").getDirectoryForWidget
const globalElementTags = require("./elementGrabberUtils").globalElementTags
const grabElementModifiableMetadata = require("./elementGrabberUtils").grabElementModifiableMetadata
const info = require("./logger").info
const isGlobal = require("./elementGrabberUtils").isGlobal
const isGrabbableElementType = require("./elementGrabberUtils").isGrabbableElementType
const makeTrackedDirectory = require("./utils").makeTrackedDirectory
const processElementInstances = require("./elementInstanceGrabber").processElementInstances
const readMetadataFromDisk = require("./metadata").readMetadataFromDisk
const request = require("./requestBuilder").request
const sanitizeName = require("./utils").sanitizeName
const splitPath = require("./utils").splitPath
const storeElementMetaData = require("./elementGrabberUtils").storeElementMetaData
const writeFileAndETag = require("./grabberUtils").writeFileAndETag
const processedWidgets = new Set()
// Need this to stop us processing the same element multiple times.
let grabbedWidgetElements
/**
* Find all the elements associated with widgets on the server and grab them.
* Global elements are handled separately.
* @returns A BlueBird promise
*/
function grabWidgetElements(directory) {
return endPointTransceiver.listWidgets().then(results =>
grabElementsForWidgets(results.data.items, directory ? splitPath(directory) : null))
}
/**
* Given a list of widgets, grab all their associated elements.
* @param widgets
* @param widget
* @returns A BlueBird promise.
*/
function grabElementsForWidgets(widgets, widgetName) {
// Make sure global element info has been loaded and we can get at global elements via an endpoint.
if (globalElementTags.size == 0 && endPointTransceiver.serverSupports("getElements")) {
return endPointTransceiver.getElements("?globals=true").then(results => {
// Walk through each of the global elements
results.data.items.forEach(globalElement => {
!globalElementTags.has(globalElement.tag) && globalElementTags.add(globalElement.tag)
})
}).then(() => {
// Now safe to grab widget elements.
return grabNonGlobalElements(widgets, widgetName)
})
} else {
// Normal case. We have been called as part of a grab.
return grabNonGlobalElements(widgets, widgetName)
}
}
/**
* Get the files for an element associated with a widget.
* @param widget
* @param element
* @param elementDir
* @returns A Bluebird promise.
*/
function grabWidgetElementAssets(widget, element, elementDir) {
// Build up a list of promises. Always try to get the template.
const promises = [
grabWidgetElementFile("getFragmentTemplate", widget.descriptor.repositoryId, element.tag, elementDir, constants.elementTemplate, "template")
]
// Try to get the javascript and metadata for the element only if the parent widget has editable JS (and there may not be any anyway).
if (widget.descriptor.jsEditable) {
promises.push(grabWidgetElementFile("getFragmentJavaScript", widget.descriptor.repositoryId, element.tag, elementDir, constants.elementJavaScript, "javascript", 500))
// Get the modifiable metadata too.
promises.push(grabElementModifiableMetadata(widget, element, elementDir))
}
return Promise.all(promises)
}
/**
* Boilerplate for grabbing elements under widgets.
* @param widgets
* @param widgetName
* @returns A BlueBird promise
*/
function grabNonGlobalElements(widgets, widgetName) {
return Promise.each(widgets, widget => {
// If we are looking for a specific widget, bail out early.
const descriptor = widget.descriptor
if (widgetName && descriptor.displayName != widgetName) {
return
}
return endPointTransceiver.getWidget([widget.repositoryId]).then(results => {
let nonInstanceElementPromise
// Make sure widget is editable and has not been processed before.
if (descriptor.editableWidget && !grabbed(descriptor.repositoryId)) {
nonInstanceElementPromise = grabNonInstanceElements(widget, results.data.fragments)
}
// Create directory for each widget instance.
const widgetInstanceDir = getWidgetInstancePath(widget.descriptor.widgetType, widget.displayName)
// See if we already have grabbed a version of the instance.
if (exists(widgetInstanceDir)) {
// Get the version from the instance we currently have on disk.
const versionOnDisk = readMetadataFromDisk(widgetInstanceDir, constants.widgetInstanceMetadataJson).version
// If the one on disk is more up to date, don't go any further.
if (versionOnDisk > widget.descriptor.version) {
return
}
}
if (nonInstanceElementPromise) {
return nonInstanceElementPromise.then(() =>
processElementInstances(widget, results.data.fragments, results.response.headers.etag))
} else {
if (descriptor.editableWidget && descriptor.layouts.length) {
return processElementInstances(widget, results.data.fragments, results.response.headers.etag)
}
}
})
})
}
/**
* Holds all the boilerplate to grab non instance element stuff.
* @param widget
* @param elements
* @returns {*|PromiseLike<T>|Promise<T>}
*/
function grabNonInstanceElements(widget, elements) {
// Make sure widget actually has some interesting elements.
if (hasGrabbableNonGlobalElements(elements)) {
const descriptor = widget.descriptor
// Create a directory for elements under the widget. Get the widgetGrabber to tell us where.
const baseElementDir =
`${getDirectoryForWidget(descriptor.widgetType, descriptor.version, descriptor.isLatestVersion)}/${constants.elementsDir}`
makeTrackedDirectory(baseElementDir)
// Clear the grabbed widget elements list.
grabbedWidgetElements = []
return Promise.map(elements, element => grabWidgetElement(widget, element, baseElementDir), getGrabbingConcurrency())
}
}
/**
* Returns true if at least one of the elements in the array is grabbable.
* @param elements
* @returns {boolean}
*/
function hasGrabbableNonGlobalElements(elements) {
return elements && elements.some((element) => isGrabbableElementType(element.type) && !isGlobal(element.tag))
}
/**
* Return true if we have already seen an instance of this widget. We need this to stop us processing the same elements
* more than once.
* @param widgetDescriptor
*/
function grabbed(widgetRespositoryId) {
if (processedWidgets.has(widgetRespositoryId)) {
return true
} else {
// Record the fact that we have seen this so the next time we return true.
processedWidgets.add(widgetRespositoryId)
return false
}
}
/**
* Holds the boilerplate for getting the file associated with an non-global element.
* @param endpoint
* @param widgetRepositoryId
* @param tag
* @param elementDir
* @param fileName
* @param field
* @returns A BlueBird promise
*/
function grabWidgetElementFile(endpoint, widgetRepositoryId, tag, elementDir, fileName, field, httpCode) {
return endPointTransceiver[endpoint]([widgetRepositoryId, tag], request().ignoring(httpCode)).tap(results => {
// Only write anything out if we got anything.
if (results.data.code && results.data.code[field]) {
writeFileAndETag(`${elementDir}/${fileName}`, results.data.code[field], results.response.headers.etag)
}
})
}
/**
* Pull down the assets associated with widgets element.
* @param widget
* @param element
* @param baseElementDir
* @returns A BlueBird promise.
*/
const grabWidgetElement = Promise.method((widget, element, baseElementDir) => {
// Figure out what the element dir would be. Get rid of any invalid characters.
const elementDir = `${baseElementDir}/${sanitizeName(element.title)}`
// Ensure element is global and of the right type and it has not already been processed.
if (!isGlobal(element.tag) && isGrabbableElementType(element.type) && !grabbedWidgetElements.includes(elementDir)) {
// Note that we have now processed the widget element.
grabbedWidgetElements.push(elementDir)
// Let the user know something is happening...
info("grabbingElement", {name: element.title})
// Then create a directory for the specific element under its widget.
makeTrackedDirectory(elementDir)
// Save off the metadata.
storeElementMetaData(element, widget.descriptor.widgetType, widget.descriptor.version, elementDir)
// Lastly get template and possibly the JavaScript and process the elements children.
return grabWidgetElementAssets(widget, element, elementDir).then(() =>
Promise.each(element.children, element => grabWidgetElement(widget, element, baseElementDir)))
}
})
exports.grabWidgetElements = grabWidgetElements