webgme-engine
Version:
WebGME server and Client API without a GUI
235 lines (209 loc) • 10.1 kB
JavaScript
/*globals define*/
/*eslint-env node, browser*/
/**
* @author pmeijer / https://github.com/pmeijer
*/
define(['q', './BlobMetadata'], function (Q, BlobMetadata) {
'use strict';
function _addMetadataAsMetadata(logger, blobClient, mainMetadata, baseName, callback) {
var orgMetadata,
metadataName = baseName + '.metadata';
return Q.ninvoke(blobClient, 'getObject', mainMetadata.content[metadataName].content)
.then(function (orgMetadataAsContent) {
// Original metadata loaded as content-file.
var contentName = baseName + '.content',
softLinkNames,
innerContentHash,
i,
contentMetadataHash;
if ((typeof Buffer !== 'undefined' && orgMetadataAsContent instanceof Buffer) ||
orgMetadataAsContent instanceof ArrayBuffer) {
orgMetadataAsContent = String.fromCharCode.apply(null,
new Uint8Array(orgMetadataAsContent));
}
logger.debug('orgMetadataAsContent', orgMetadataAsContent);
orgMetadata = JSON.parse(orgMetadataAsContent);
if (orgMetadata.contentType === BlobMetadata.CONTENT_TYPES.OBJECT) {
contentMetadataHash = mainMetadata.content[contentName].content;
logger.debug('contentMetadataHash', contentMetadataHash);
return Q.ninvoke(blobClient, 'getMetadata', contentMetadataHash)
.then(function (contentMetadata) {
// The uploaded contents metadata loaded - ensure original meta data matches...
if (contentMetadata.size !== orgMetadata.size ||
contentMetadata.content !== orgMetadata.content) {
throw new Error('Matching content was not uploaded for metadata',
contentMetadata, orgMetadata);
}
// it did so upload the original metadata as an actual metadata.
});
} else if (orgMetadata.contentType === BlobMetadata.CONTENT_TYPES.COMPLEX) {
// Just make sure that the metadata exists among the uploaded files
// (their consistencies will be check).
softLinkNames = Object.keys(orgMetadata.content);
for (i = 0; i < softLinkNames.length; i += 1) {
innerContentHash = orgMetadata.content[softLinkNames[i]].content;
if (Object.hasOwn(mainMetadata.content, innerContentHash + '.metadata') === false) {
throw new Error('Complex object softLink does not have attached .metadata!');
}
}
} else {
throw new Error('Unsupported content type', orgMetadata.contentType);
}
})
.then(function () {
return Q.ninvoke(blobClient, 'putMetadata', orgMetadata);
})
.nodeify(callback);
}
/**
*
* @param {GmeLogger} logger
* @param {BlobClient} blobClient
* @param {object} mainMetadata
* @param callback
* @returns {*}
*/
function addAssetsFromExportedProject(logger, blobClient, mainMetadata, callback) {
var projectFileHash = null,
softLinkNames = Object.keys(mainMetadata.content),
deferred = Q.defer();
Q.allSettled(softLinkNames.map(function (softLinkName) {
var softLinkPieces = softLinkName.split('.'),
type = softLinkPieces.pop();
if (type === 'json') {
projectFileHash = mainMetadata.content[softLinkName].content;
} else if (type === 'metadata') {
return _addMetadataAsMetadata(logger, blobClient, mainMetadata, softLinkPieces.pop());
}
}))
.then(function (result) {
var i,
error;
for (i = 0; i < result.length; i += 1) {
if (result[i].state === 'rejected') {
logger.error('Adding asset failed with error', softLinkNames[i], result[i].reason);
error = 'Failed adding some of the assets, see error logs';
}
}
if (error) {
throw new Error(error);
}
return deferred.resolve(projectFileHash);
})
.catch(deferred.reject);
return deferred.promise.nodeify(callback);
}
function _gatherFilesFromMetadataHashRec(logger, blobClient, metadata, assetHash, artifact) {
var deferred = Q.defer(),
filenameMetadata = assetHash + '.metadata',
softLinkNames,
filenameContent;
logger.debug('_gatherFilesFromMetadataHashRec, metadata:', metadata);
if (metadata.contentType === BlobMetadata.CONTENT_TYPES.OBJECT) {
filenameContent = assetHash + '.content';
Q.ninvoke(artifact, 'addMetadataHash', filenameContent, assetHash, metadata.size)
.then(function () {
return Q.ninvoke(artifact, 'addFile', filenameMetadata, JSON.stringify(metadata));
})
.then(function () {
deferred.resolve();
})
.catch(function (err) {
if (err.message.indexOf('Another content with the same name was already added.') > -1) {
deferred.resolve();
} else {
deferred.reject(err);
}
});
} else if (metadata.contentType === BlobMetadata.CONTENT_TYPES.COMPLEX) {
// Add .metadata and .content for all linked soft-links (recursively).
softLinkNames = Object.keys(metadata.content);
Q.all(softLinkNames.map(function (softLinkName) {
var softLinkMetadataHash = metadata.content[softLinkName].content;
logger.debug('Complex object, softLinkMetadataHash:', softLinkMetadataHash);
return Q.ninvoke(blobClient, 'getMetadata', softLinkMetadataHash)
.then(function (softLinkMetadata) {
return _gatherFilesFromMetadataHashRec(logger, blobClient,
softLinkMetadata, softLinkMetadataHash, artifact);
});
}))
.then(function () {
// Finally add the .metadata for the complex object.
return Q.ninvoke(artifact, 'addFile', filenameMetadata, JSON.stringify(metadata));
})
.then(function () {
deferred.resolve();
})
.catch(deferred.reject);
} else {
deferred.reject(new Error('Unsupported content type: ' + metadata.contentType));
}
return deferred.promise;
}
/**
*
* @param {GmeLogger} logger
* @param {BlobClient} blobClient
* @param {object} jsonExport
* @param {boolean} addAssets
* @param callback
* @returns {*}
*/
function buildProjectPackage(logger, blobClient, jsonExport, addAssets, filename, callback) {
var artie = blobClient.createArtifact(jsonExport.projectId +
'_' + (jsonExport.branchName || jsonExport.commitHash)),
assets = jsonExport.hashes.assets || [],
deferred = Q.defer();
artie.descriptor.name = filename || (jsonExport.projectId +
'_' + (jsonExport.commitHash || '').substr(1, 6) + '.webgmex');
if (!addAssets) {
assets = [];
}
function getMetadataSafely(assetHash) {
return blobClient.getMetadata(assetHash)
.catch(function (err) {
logger.warn('When building project package could not retrieve metadata for attribute with value',
assetHash, '. Will continue assuming it is not an asset attribute...');
logger.debug('Returned error when getMetadata', err);
return null;
});
}
Q.allSettled(assets.map(function (assetHash) {
return getMetadataSafely(assetHash)
.then(function (metadata) {
if (metadata) {
return _gatherFilesFromMetadataHashRec(logger, blobClient, metadata, assetHash, artie);
} else {
return Q();
}
});
}))
.then(function (result) {
var error,
i;
for (i = 0; i < result.length; i += 1) {
if (result[i].state === 'rejected') {
error = result[i].reason;
logger.debug('Gathering returned with error', assets[i], error);
if (error.message.indexOf('Another content with the same name was already added.') === -1) {
//some real error
throw new Error('gathering assets [' + assets[i] + '] failed:' + error.message);
}
}
}
})
.then(function () {
return artie.addFile('project.json', JSON.stringify(jsonExport));
})
.then(function () {
return artie.save();
})
.then(deferred.resolve)
.catch(deferred.reject);
return deferred.promise.nodeify(callback);
}
return {
addAssetsFromExportedProject: addAssetsFromExportedProject,
buildProjectPackage: buildProjectPackage
};
});