RepoRoleSettings RepoBSONFactory::makeRepoRoleSettings(
	const std::string &uniqueRoleName,
	const std::string &color,
	const std::string &description,
	const std::vector<std::string> &modules)
{
	RepoBSONBuilder builder;

	//--------------------------------------------------------------------------
	// Project name
	if (!uniqueRoleName.empty())
		builder << REPO_LABEL_ID << uniqueRoleName;

	// Color
	if (!color.empty())
		builder << REPO_LABEL_COLOR << color;

	// Description
	if (!description.empty())
		builder << REPO_LABEL_DESCRIPTION << description;

	// Modules
	if (modules.size() > 0)
		builder.appendArray(REPO_LABEL_MODULES, modules);

	return RepoRoleSettings(builder.obj());
}
TEST(RepoBSONTest, AppendDefaultsTest)
{
	RepoBSONBuilder builder;

	RepoBSONFactory::appendDefaults(
		builder, "test");

	RepoNode n = builder.obj();

	EXPECT_FALSE(n.isEmpty());

	EXPECT_EQ(4, n.nFields()); 

	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_ID));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_SHARED_ID));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_API));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_TYPE));

	//Ensure existing fields doesnt' disappear

	RepoBSONBuilder builderWithFields;

	builderWithFields << "Number" << 1023;
	builderWithFields << "doll" << "Kitty";

	RepoBSONFactory::appendDefaults(
		builderWithFields, "test");

}
Exemple #3
0
bool RepoScene::loadStash(
	repo::core::handler::AbstractDatabaseHandler *handler,
	std::string &errMsg){
	bool success = true;

	if (!handler)
	{
		errMsg += "Trying to load stash graph without a database handler!";
		return false;
	}

	if (!revNode){
		if (!loadRevision(handler, errMsg)) return false;
	}

	//Get the relevant nodes from the scene graph using the unique IDs stored in this revision node
	RepoBSONBuilder builder;
	builder.append(REPO_NODE_STASH_REF, revNode->getUniqueID());

	std::vector<RepoBSON> nodes = handler->findAllByCriteria(databaseName, projectName + "." + stashExt, builder.obj());
	if (success = nodes.size())
	{
		repoInfo << "# of nodes in this stash scene = " << nodes.size();
		success = populate(GraphType::OPTIMIZED, handler, nodes, errMsg);
	}
	else
	{
		errMsg += "stash is empty";
	}

	return  success;
}
MetadataNode RepoBSONFactory::makeMetaDataNode(
	RepoBSON			          &metadata,
	const std::string             &mimeType,
	const std::string             &name,
	const std::vector<repoUUID>  &parents,
	const int                     &apiLevel)
{
	RepoBSONBuilder builder;

	// Compulsory fields such as _id, type, api as well as path
	// and optional name
	auto defaults = appendDefaults(REPO_NODE_TYPE_METADATA, apiLevel, generateUUID(), name, parents);
	builder.appendElements(defaults);

	//--------------------------------------------------------------------------
	// Media type
	if (!mimeType.empty())
		builder << REPO_LABEL_MEDIA_TYPE << mimeType;

	//--------------------------------------------------------------------------
	// Add metadata subobject
	if (!metadata.isEmpty())
		builder << REPO_NODE_LABEL_METADATA << metadata;

	return MetadataNode(builder.obj());
}
RepoProjectSettings RepoBSONFactory::makeRepoProjectSettings(
	const std::string &uniqueProjectName,
	const std::string &owner,
	const std::string &type,
	const std::string &description,
	const double pinSize,
	const double avatarHeight,
	const double visibilityLimit,
	const double speed,
	const double zNear,
	const double zFar)
{
	RepoBSONBuilder builder;

	//--------------------------------------------------------------------------
	// Project name
	if (!uniqueProjectName.empty())
		builder << REPO_LABEL_ID << uniqueProjectName;

	//--------------------------------------------------------------------------
	// Owner
	if (!owner.empty())
		builder << REPO_LABEL_OWNER << owner;

	//--------------------------------------------------------------------------
	// Description
	if (!description.empty())
		builder << REPO_LABEL_DESCRIPTION << description;

	//--------------------------------------------------------------------------
	// Type
	if (!type.empty())
		builder << REPO_LABEL_TYPE << type;

	//--------------------------------------------------------------------------
	// Properties (embedded sub-bson)
	RepoBSONBuilder propertiesBuilder;
	if (pinSize != REPO_DEFAULT_PROJECT_PIN_SIZE)
		propertiesBuilder << REPO_LABEL_PIN_SIZE << pinSize;
	if (avatarHeight != REPO_DEFAULT_PROJECT_AVATAR_HEIGHT)
		propertiesBuilder << REPO_LABEL_AVATAR_HEIGHT << avatarHeight;
	if (visibilityLimit != REPO_DEFAULT_PROJECT_VISIBILITY_LIMIT)
		propertiesBuilder << REPO_LABEL_VISIBILITY_LIMIT << visibilityLimit;
	if (speed != REPO_DEFAULT_PROJECT_SPEED)
		propertiesBuilder << REPO_LABEL_SPEED << speed;
	if (zNear != REPO_DEFAULT_PROJECT_ZNEAR)
		propertiesBuilder << REPO_LABEL_ZNEAR << zNear;
	if (zFar != REPO_DEFAULT_PROJECT_ZFAR)
		propertiesBuilder << REPO_LABEL_ZFAR << zFar;
	RepoBSON propertiesBSON = propertiesBuilder.obj();
	if (propertiesBSON.isValid() && !propertiesBSON.isEmpty())
		builder << REPO_LABEL_PROPERTIES << propertiesBSON;

	//--------------------------------------------------------------------------
	// Add to the parent object
	return RepoProjectSettings(builder.obj());
}
TransformationNode RepoBSONFactory::makeTransformationNode(
	const std::vector<std::vector<float>> &transMatrix,
	const std::string                     &name,
	const std::vector<repoUUID>		  &parents,
	const int                             &apiLevel)
{
	RepoBSONBuilder builder;

	auto defaults = appendDefaults(REPO_NODE_TYPE_TRANSFORMATION, apiLevel, generateUUID(), name, parents);
	builder.appendElements(defaults);

	//--------------------------------------------------------------------------
	// Store matrix as array of arrays
	uint32_t matrixSize = 4;
	RepoBSONBuilder rows;
	for (uint32_t i = 0; i < transMatrix.size(); ++i)
	{
		RepoBSONBuilder columns;
		for (uint32_t j = 0; j < transMatrix[i].size(); ++j){
			columns << std::to_string(j) << transMatrix[i][j];
		}
		rows.appendArray(std::to_string(i), columns.obj());
	}
	builder.appendArray(REPO_NODE_LABEL_MATRIX, rows.obj());

	return TransformationNode(builder.obj());
}
Exemple #7
0
bool RepoScene::commitStash(
	repo::core::handler::AbstractDatabaseHandler *handler,
	std::string &errMsg)
{
	/*
	* Don't bother if:
	* 1. root node is null (not instantiated)
	* 2. revnode is null (unoptimised scene graph needs to be commited first
	*/

	repoUUID rev;
	if (!handler)
	{
		errMsg += "Cannot commit stash graph - nullptr to database handler.";
		return false;
	}
	if (!revNode)
	{
		errMsg += "Revision node not found, make sure the default scene graph is commited";
		return false;
	}
	else
	{
		rev = revNode->getUniqueID();
	}
	if (stashGraph.rootNode)
	{
		updateRevisionStatus(handler, repo::core::model::RevisionNode::UploadStatus::GEN_REPO_STASH);
		//Add rev id onto the stash nodes before committing.
		std::vector<repoUUID> nodes;
		RepoBSONBuilder builder;
		builder.append(REPO_NODE_STASH_REF, rev);
		RepoBSON revID = builder.obj(); // this should be RepoBSON?

		for (auto &pair : stashGraph.nodesByUniqueID)
		{
			nodes.push_back(pair.first);
			*pair.second = pair.second->cloneAndAddFields(&revID, false);
		}

		auto success = commitNodes(handler, nodes, GraphType::OPTIMIZED, errMsg);

		if (success)
			updateRevisionStatus(handler, repo::core::model::RevisionNode::UploadStatus::COMPLETE);

		return success;
	}
	else
	{
		//Not neccessarily an error. Make it visible for debugging purposes
		repoDebug << "Stash graph not commited. Root node is nullptr!";
		return true;
	}
}
RepoRole RepoBSONFactory::_makeRepoRole(
	const std::string &roleName,
	const std::string &database,
	const std::vector<RepoPrivilege> &privileges,
	const std::vector<std::pair<std::string, std::string> > &inheritedRoles
	)
{
	RepoBSONBuilder builder;
	builder << REPO_LABEL_ID << database + "." + roleName;
	builder << REPO_ROLE_LABEL_ROLE << roleName;
	builder << REPO_ROLE_LABEL_DATABASE << database;

	//====== Add Privileges ========
	if (privileges.size() > 0)
	{
		RepoBSONBuilder privilegesBuilder;
		for (size_t i = 0; i < privileges.size(); ++i)
		{
			const auto &p = privileges[i];
			RepoBSONBuilder innerBsonBuilder, actionBuilder;
			RepoBSON resource = BSON(REPO_ROLE_LABEL_DATABASE << p.database << REPO_ROLE_LABEL_COLLECTION << p.collection);
			innerBsonBuilder << REPO_ROLE_LABEL_RESOURCE << resource;

			for (size_t aCount = 0; aCount < p.actions.size(); ++aCount)
			{
				actionBuilder << std::to_string(aCount) << RepoRole::dbActionToString(p.actions[aCount]);
			}

			innerBsonBuilder.appendArray(REPO_ROLE_LABEL_ACTIONS, actionBuilder.obj());

			privilegesBuilder << std::to_string(i) << innerBsonBuilder.obj();
		}
		builder.appendArray(REPO_ROLE_LABEL_PRIVILEGES, privilegesBuilder.obj());
	}
	else
	{
		repoDebug << "Creating a role with no privileges!";
	}

	//====== Add Inherited Roles ========

	if (inheritedRoles.size() > 0)
	{
		RepoBSONBuilder inheritedRolesBuilder;

		for (size_t i = 0; i < inheritedRoles.size(); ++i)
		{
			RepoBSON parentRole = BSON(
				REPO_ROLE_LABEL_ROLE << inheritedRoles[i].second
				<< REPO_ROLE_LABEL_DATABASE << inheritedRoles[i].first
				);

			inheritedRolesBuilder << std::to_string(i) << parentRole;
		}

		builder.appendArray(REPO_ROLE_LABEL_INHERITED_ROLES, inheritedRolesBuilder.obj());
	}

	return RepoRole(builder.obj());
}
TransformationNode RepoBSONFactory::makeTransformationNode(
	const repo::lib::RepoMatrix &transMatrix,
	const std::string                     &name,
	const std::vector<repo::lib::RepoUUID>		  &parents,
	const int                             &apiLevel)
{
	RepoBSONBuilder builder;

	auto defaults = appendDefaults(REPO_NODE_TYPE_TRANSFORMATION, apiLevel, repo::lib::RepoUUID::createUUID(), name, parents);
	builder.appendElements(defaults);

	builder.append(REPO_NODE_LABEL_MATRIX, transMatrix);	

	return TransformationNode(builder.obj());
}
RepoBSON RepoBSONFactory::appendDefaults(
	const std::string &type,
	const unsigned int api,
	const repoUUID &sharedId,
	const std::string &name,
	const std::vector<repoUUID> &parents,
	const repoUUID &uniqueID)
{
	RepoBSONBuilder builder;
	uint64_t bytesize = 0;

	//--------------------------------------------------------------------------
	// ID field (UUID)
	builder.append(REPO_NODE_LABEL_ID, uniqueID);

	//--------------------------------------------------------------------------
	// Shared ID (UUID)
	builder.append(REPO_NODE_LABEL_SHARED_ID, sharedId);

	bytesize += 2 * sizeof(repoUUID);

	//--------------------------------------------------------------------------
	// Type
	if (!type.empty())
	{
		builder << REPO_NODE_LABEL_TYPE << type;
	}

	//--------------------------------------------------------------------------
	// API level
	builder << REPO_NODE_LABEL_API << api;

	//--------------------------------------------------------------------------
	// Parents
	if (parents.size() > 0)
	{
		builder.appendArray(REPO_NODE_LABEL_PARENTS, parents);
	}

	//--------------------------------------------------------------------------
	// Name
	if (!name.empty())
	{
		builder << REPO_NODE_LABEL_NAME << name;
	}

	return builder.obj();
}
Exemple #11
0
RepoNode RepoNode::cloneAndAddFields(
	const RepoBSON *changes,
	const bool     &newUniqueID) const
{
	RepoBSONBuilder builder;
	if (newUniqueID)
	{
		builder.append(REPO_NODE_LABEL_ID, generateUUID());
	}

	builder.appendElementsUnique(*changes);

	builder.appendElementsUnique(*this);

	return RepoNode(builder.obj(), bigFiles);
}
TextureNode RepoBSONFactory::makeTextureNode(
	const std::string &name,
	const char        *data,
	const uint32_t    &byteCount,
	const uint32_t    &width,
	const uint32_t    &height,
	const int         &apiLevel)
{
	RepoBSONBuilder builder;
	auto defaults = appendDefaults(REPO_NODE_TYPE_TEXTURE, apiLevel, generateUUID(), name);
	builder.appendElements(defaults);
	//
	// Width
	//
	builder << REPO_LABEL_WIDTH << width;

	//
	// Height
	//
	builder << REPO_LABEL_HEIGHT << height;

	//
	// Format TODO: replace format with MIME Type?
	//
	if (!name.empty())
	{
		boost::filesystem::path file{ name };
		std::string ext = file.extension().string();
		if (!ext.empty())
			builder << REPO_NODE_LABEL_EXTENSION << ext.substr(1, ext.size());
	}
	//
	// Data
	//

	if (data && byteCount)
		builder.appendBinary(
		REPO_LABEL_DATA,
		data,
		byteCount);
	else
	{
		repoWarning << " Creating a texture node with no texture!";
	}

	return TextureNode(builder.obj());
}
Exemple #13
0
RepoNode RepoNode::cloneAndAddParent(
	const std::vector<repoUUID> &parentIDs) const
{
	RepoBSONBuilder builder;
	RepoBSONBuilder arrayBuilder;

	std::vector<repoUUID> currentParents = getParentIDs();
	currentParents.insert(currentParents.end(), parentIDs.begin(), parentIDs.end());

	std::sort(currentParents.begin(), currentParents.end());
	auto last = std::unique(currentParents.begin(), currentParents.end());
	if (last != currentParents.end())
		currentParents.erase(last, currentParents.end());

	builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents);

	builder.appendElementsUnique(*this);

	return RepoNode(builder.obj(), bigFiles);
}
RepoNode CameraNode::cloneAndApplyTransformation(
	const std::vector<float> &matrix) const
{
	RepoBSONBuilder builder;
	if (hasField(REPO_NODE_LABEL_LOOK_AT))
	{
		builder.append(REPO_NODE_LABEL_LOOK_AT, multiplyMatVec(matrix, getLookAt()));
	}

	if (hasField(REPO_NODE_LABEL_POSITION))
	{
		builder.append(REPO_NODE_LABEL_POSITION, multiplyMatVec(matrix, getPosition()));
	}

	if (hasField(REPO_NODE_LABEL_UP))
	{
		builder.append(REPO_NODE_LABEL_UP, multiplyMatVec(matrix, getUp()));
	}
	return CameraNode(builder.appendElementsUnique(*this));
}
Exemple #15
0
RepoNode RepoNode::cloneAndAddParent(
	const repoUUID &parentID,
	const bool     &newUniqueID,
	const bool     &newSharedID,
	const bool     &overwrite) const
{
	RepoBSONBuilder builder;
	RepoBSONBuilder arrayBuilder;

	std::vector<repoUUID> currentParents;
	if (!overwrite)
	{
		currentParents = getParentIDs();
	}

	if (std::find(currentParents.begin(), currentParents.end(), parentID) == currentParents.end())
		currentParents.push_back(parentID);
	builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents);

	if (newUniqueID)
		builder.append(REPO_NODE_LABEL_ID, generateUUID());

	if (newSharedID)
		builder.append(REPO_NODE_LABEL_SHARED_ID, generateUUID());

	builder.appendElementsUnique(*this);

	return RepoNode(builder.obj(), bigFiles);
}
Exemple #16
0
RepoNode RepoNode::cloneAndRemoveParent(
	const repoUUID &parentID,
	const bool     &newUniqueID) const
{
	RepoBSONBuilder builder;
	RepoBSONBuilder arrayBuilder;

	std::vector<repoUUID> currentParents = getParentIDs();
	auto parentIdx = std::find(currentParents.begin(), currentParents.end(), parentID);
	if (parentIdx != currentParents.end())
	{
		currentParents.erase(parentIdx);
		if (newUniqueID)
		{
			builder.append(REPO_NODE_LABEL_ID, generateUUID());
		}
	}
	else
	{
		repoWarning << "Trying to remove a parent that isn't really a parent!";
	}
	if (currentParents.size() > 0)
	{
		builder.appendArray(REPO_NODE_LABEL_PARENTS, currentParents);
		builder.appendElementsUnique(*this);
	}
	else
	{
		builder.appendElementsUnique(removeField(REPO_NODE_LABEL_PARENTS));
	}

	return RepoNode(builder.obj(), bigFiles);
}
Exemple #17
0
bool RepoScene::loadRevision(
	repo::core::handler::AbstractDatabaseHandler *handler,
	std::string &errMsg){
	bool success = true;

	if (!handler)
	{
		errMsg = "Cannot load revision with an empty database handler";
		return false;
	}

	RepoBSON bson;
	repoTrace << "loading revision : " << databaseName << "." << projectName << " head Revision: " << headRevision;
	if (headRevision){
		RepoBSONBuilder critBuilder;
		critBuilder.append(REPO_NODE_LABEL_SHARED_ID, branch);
		critBuilder << REPO_NODE_REVISION_LABEL_INCOMPLETE << BSON("$exists" << false);

		bson = handler->findOneByCriteria(databaseName, projectName + "." +
			revExt, critBuilder.obj(), REPO_NODE_REVISION_LABEL_TIMESTAMP);
		repoTrace << "Fetching head of revision from branch " << UUIDtoString(branch);
	}
	else{
		bson = handler->findOneByUniqueID(databaseName, projectName + "." + revExt, revision);
		repoTrace << "Fetching revision using unique ID: " << UUIDtoString(revision);
	}

	if (bson.isEmpty()){
		errMsg = "Failed: cannot find revision document from " + databaseName + "." + projectName + "." + revExt;
		success = false;
	}
	else{
		revNode = new RevisionNode(bson);
		worldOffset = revNode->getCoordOffset();
	}

	return success;
}
ReferenceNode RepoBSONFactory::makeReferenceNode(
	const std::string &database,
	const std::string &project,
	const repoUUID    &revisionID,
	const bool        &isUniqueID,
	const std::string &name,
	const int         &apiLevel)
{
	RepoBSONBuilder builder;
	std::string nodeName = name.empty() ? database + "." + project : name;

	auto defaults = appendDefaults(REPO_NODE_TYPE_REFERENCE, apiLevel, generateUUID(), nodeName);
	builder.appendElements(defaults);

	//--------------------------------------------------------------------------
	// Project owner (company or individual)
	if (!database.empty())
		builder << REPO_NODE_REFERENCE_LABEL_OWNER << database;

	//--------------------------------------------------------------------------
	// Project name
	if (!project.empty())
		builder << REPO_NODE_REFERENCE_LABEL_PROJECT << project;

	//--------------------------------------------------------------------------
	// Revision ID (specific revision if UID, branch if SID)
	builder.append(
		REPO_NODE_REFERENCE_LABEL_REVISION_ID,
		revisionID);

	//--------------------------------------------------------------------------
	// Unique set if the revisionID is UID, not set if SID (branch)
	if (isUniqueID)
		builder << REPO_NODE_REFERENCE_LABEL_UNIQUE << isUniqueID;

	return ReferenceNode(builder.obj());
}
RepoBSON MeshNode::meshMappingAsBSON(const repo_mesh_mapping_t  &mapping)
{
	RepoBSONBuilder builder;
	builder.append(REPO_NODE_MESH_LABEL_MAP_ID, mapping.mesh_id);
	builder.append(REPO_NODE_MESH_LABEL_MATERIAL_ID, mapping.material_id);
	builder << REPO_NODE_MESH_LABEL_VERTEX_FROM << mapping.vertFrom;
	builder << REPO_NODE_MESH_LABEL_VERTEX_TO << mapping.vertTo;
	builder << REPO_NODE_MESH_LABEL_TRIANGLE_FROM << mapping.triFrom;
	builder << REPO_NODE_MESH_LABEL_TRIANGLE_TO << mapping.triTo;

	RepoBSONBuilder bbBuilder;
	bbBuilder.append("0", mapping.min);
	bbBuilder.append("1", mapping.max);

	builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, bbBuilder.obj());

	return builder.obj();
}
CameraNode RepoBSONFactory::makeCameraNode(
	const float         &aspectRatio,
	const float         &farClippingPlane,
	const float         &nearClippingPlane,
	const float         &fieldOfView,
	const repo_vector_t &lookAt,
	const repo_vector_t &position,
	const repo_vector_t &up,
	const std::string   &name,
	const int           &apiLevel)
{
	RepoBSONBuilder builder;

	//--------------------------------------------------------------------------
	// Compulsory fields such as _id, type, api as well as path
	// and optional name
	auto defaults = appendDefaults(REPO_NODE_TYPE_CAMERA, apiLevel, generateUUID(), name);
	builder.appendElements(defaults);

	//--------------------------------------------------------------------------
	// Aspect ratio
	builder << REPO_NODE_LABEL_ASPECT_RATIO << aspectRatio;

	//--------------------------------------------------------------------------
	// Far clipping plane
	builder << REPO_NODE_LABEL_FAR << farClippingPlane;

	//--------------------------------------------------------------------------
	// Near clipping plane
	builder << REPO_NODE_LABEL_NEAR << nearClippingPlane;

	//--------------------------------------------------------------------------
	// Field of view
	builder << REPO_NODE_LABEL_FOV << fieldOfView;

	//--------------------------------------------------------------------------
	// Look at vector
	builder.append(REPO_NODE_LABEL_LOOK_AT, lookAt);

	//--------------------------------------------------------------------------
	// Position vector
	builder.append(REPO_NODE_LABEL_POSITION, position);

	//--------------------------------------------------------------------------
	// Up vector
	builder.append(REPO_NODE_LABEL_UP, up);

	return CameraNode(builder.obj());
}
TEST(RepoBSONFactoryTest, AppendDefaultsTest)
{
	RepoBSONBuilder builder;

	auto defaults = RepoBSONFactory::appendDefaults("test");
	builder.appendElements(defaults);

	RepoNode n = builder.obj();

	EXPECT_FALSE(n.isEmpty());

	EXPECT_EQ(4, n.nFields());

	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_ID));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_SHARED_ID));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_API));
	EXPECT_TRUE(n.hasField(REPO_NODE_LABEL_TYPE));

	//Ensure existing fields doesnt' disappear

	RepoBSONBuilder builderWithFields;

	builderWithFields << "Number" << 1023;
	builderWithFields << "doll" << "Kitty";

	auto defaults2 = RepoBSONFactory::appendDefaults("test");
	builderWithFields.appendElements(defaults2);

	RepoNode nWithExists = builderWithFields.obj();
	EXPECT_FALSE(nWithExists.isEmpty());

	EXPECT_EQ(6, nWithExists.nFields());

	EXPECT_TRUE(nWithExists.hasField(REPO_NODE_LABEL_ID));
	EXPECT_TRUE(nWithExists.hasField(REPO_NODE_LABEL_SHARED_ID));
	EXPECT_TRUE(nWithExists.hasField(REPO_NODE_LABEL_API));
	EXPECT_TRUE(nWithExists.hasField(REPO_NODE_LABEL_TYPE));

	EXPECT_TRUE(nWithExists.hasField("doll"));
	EXPECT_EQ("Kitty", std::string(nWithExists.getStringField("doll")));
	EXPECT_TRUE(nWithExists.hasField("Number"));
	EXPECT_EQ(nWithExists.getField("Number").Int(), 1023);
}
MaterialNode RepoBSONFactory::makeMaterialNode(
	const repo_material_t &material,
	const std::string     &name,
	const int             &apiLevel)
{
	RepoBSONBuilder builder;

	// Compulsory fields such as _id, type, api as well as path
	// and optional name
	auto defaults = appendDefaults(REPO_NODE_TYPE_MATERIAL, apiLevel, generateUUID(), name);
	builder.appendElements(defaults);

	if (material.ambient.size() > 0)
		builder.appendArray(REPO_NODE_MATERIAL_LABEL_AMBIENT, material.ambient);
	if (material.diffuse.size() > 0)
		builder.appendArray(REPO_NODE_MATERIAL_LABEL_DIFFUSE, material.diffuse);
	if (material.specular.size() > 0)
		builder.appendArray(REPO_NODE_MATERIAL_LABEL_SPECULAR, material.specular);
	if (material.emissive.size() > 0)
		builder.appendArray(REPO_NODE_MATERIAL_LABEL_EMISSIVE, material.emissive);

	if (material.isWireframe)
		builder << REPO_NODE_MATERIAL_LABEL_WIREFRAME << material.isWireframe;
	if (material.isTwoSided)
		builder << REPO_NODE_MATERIAL_LABEL_TWO_SIDED << material.isTwoSided;

	if (material.opacity == material.opacity)
		builder << REPO_NODE_MATERIAL_LABEL_OPACITY << material.opacity;

	if (material.shininess == material.shininess)
		builder << REPO_NODE_MATERIAL_LABEL_SHININESS << material.shininess;

	if (material.shininessStrength == material.shininessStrength)
		builder << REPO_NODE_MATERIAL_LABEL_SHININESS_STRENGTH << material.shininessStrength;

	return MaterialNode(builder.obj());
}
RevisionNode RepoBSONFactory::makeRevisionNode(
	const std::string			   &user,
	const repoUUID                 &branch,
	const std::vector<repoUUID>    &currentNodes,
	//const std::vector<repoUUID>    &added,
	//const std::vector<repoUUID>    &removed,
	//const std::vector<repoUUID>    &modified,
	const std::vector<std::string> &files,
	const std::vector<repoUUID>    &parent,
	const std::vector<double>    &worldOffset,
	const std::string              &message,
	const std::string              &tag,
	const int                      &apiLevel
	)
{
	RepoBSONBuilder builder;
	repoUUID uniqueID = generateUUID();

	//--------------------------------------------------------------------------
	// Compulsory fields such as _id, type, api as well as path
	auto defaults = appendDefaults(REPO_NODE_TYPE_REVISION, apiLevel, branch, "", parent, uniqueID);
	builder.appendElements(defaults);

	//--------------------------------------------------------------------------
	// Author
	if (!user.empty())
		builder << REPO_NODE_REVISION_LABEL_AUTHOR << user;

	//--------------------------------------------------------------------------
	// Message
	if (!message.empty())
		builder << REPO_NODE_REVISION_LABEL_MESSAGE << message;

	//--------------------------------------------------------------------------
	// Tag
	if (!tag.empty())
		builder << REPO_NODE_REVISION_LABEL_TAG << tag;

	//--------------------------------------------------------------------------
	// Timestamp
	builder.appendTimeStamp(REPO_NODE_REVISION_LABEL_TIMESTAMP);

	//--------------------------------------------------------------------------

	// Current Unique IDs
	if (currentNodes.size() > 0)
		builder.appendArray(REPO_NODE_REVISION_LABEL_CURRENT_UNIQUE_IDS, currentNodes);

	//--------------------------------------------------------------------------
	// Shift for world coordinates
	if (worldOffset.size() > 0)
		builder.appendArray(REPO_NODE_REVISION_LABEL_WORLD_COORD_SHIFT, worldOffset);

	////--------------------------------------------------------------------------
	//// Added Shared IDs

	//if (added.size() > 0)
	//	builder.appendArray(REPO_NODE_REVISION_LABEL_ADDED_SHARED_IDS, builder.createArrayBSON(added));

	////--------------------------------------------------------------------------
	//// Deleted Shared IDs
	//if (removed.size() > 0)
	//	builder.appendArray(REPO_NODE_REVISION_LABEL_DELETED_SHARED_IDS, builder.createArrayBSON(removed));

	////--------------------------------------------------------------------------
	//// Modified Shared IDs
	//if (modified.size() > 0)
	//	builder.appendArray(REPO_NODE_REVISION_LABEL_MODIFIED_SHARED_IDS, builder.createArrayBSON(modified));
	//--------------------------------------------------------------------------

	//--------------------------------------------------------------------------
	// original files references
	if (files.size() > 0)
	{
		std::string uniqueIDStr = UUIDtoString(uniqueID);
		mongo::BSONObjBuilder arrbuilder;
		for (int i = 0; i < files.size(); ++i)
		{
			arrbuilder << std::to_string(i) << uniqueIDStr + files[i];
		}

		builder.appendArray(REPO_NODE_REVISION_LABEL_REF_FILE, arrbuilder.obj());
	}

	return RevisionNode(builder.obj());
}
RepoUser RepoBSONFactory::makeRepoUser(
	const std::string                           &userName,
	const std::string                           &password,
	const std::string                           &firstName,
	const std::string                           &lastName,
	const std::string                           &email,
	const std::list<std::pair<std::string, std::string>>   &roles,
	const std::list<std::pair<std::string, std::string>>   &apiKeys,
	const std::vector<char>                     &avatar)
{
	RepoBSONBuilder builder;
	RepoBSONBuilder customDataBuilder;

	builder.append(REPO_LABEL_ID, generateUUID());
	if (!userName.empty())
		builder << REPO_USER_LABEL_USER << userName;

	if (!password.empty())
	{
		RepoBSONBuilder credentialsBuilder;
		credentialsBuilder << REPO_USER_LABEL_CLEARTEXT << password;
		builder << REPO_USER_LABEL_CREDENTIALS << credentialsBuilder.obj();
	}

	if (!firstName.empty())
		customDataBuilder << REPO_USER_LABEL_FIRST_NAME << firstName;

	if (!lastName.empty())
		customDataBuilder << REPO_USER_LABEL_LAST_NAME << lastName;

	if (!email.empty())
		customDataBuilder << REPO_USER_LABEL_EMAIL << email;

	if (!apiKeys.empty())
		customDataBuilder.appendArrayPair(REPO_USER_LABEL_API_KEYS, apiKeys, REPO_USER_LABEL_LABEL, REPO_USER_LABEL_KEY);

	if (avatar.size())
	{
		RepoBSONBuilder avatarBuilder;
		avatarBuilder.appendBinary(REPO_LABEL_DATA, &avatar.at(0), sizeof(avatar.at(0))*avatar.size());
		customDataBuilder << REPO_LABEL_AVATAR << avatarBuilder.obj();
	}

	builder << REPO_USER_LABEL_CUSTOM_DATA << customDataBuilder.obj();

	if (roles.size())
		builder.appendArrayPair(REPO_USER_LABEL_ROLES, roles, REPO_USER_LABEL_DB, REPO_USER_LABEL_ROLE);

	return RepoUser(builder.obj());
}
RepoNode MeshNode::cloneAndApplyTransformation(
	const std::vector<float> &matrix) const
{
	std::vector<repo_vector_t> vertices = getVertices();
	std::vector<repo_vector_t> normals = getNormals();

	auto newBigFiles = bigFiles;

	RepoBSONBuilder builder;
	std::vector<repo_vector_t> resultVertice;
	std::vector<repo_vector_t> newBbox;
	if (vertices.size())
	{
		resultVertice.reserve(vertices.size());
		for (const repo_vector_t &v : vertices)
		{
			resultVertice.push_back(multiplyMatVec(matrix, v));
			if (newBbox.size())
			{
				if (resultVertice.back().x < newBbox[0].x)
					newBbox[0].x = resultVertice.back().x;

				if (resultVertice.back().y < newBbox[0].y)
					newBbox[0].y = resultVertice.back().y;

				if (resultVertice.back().z < newBbox[0].z)
					newBbox[0].z = resultVertice.back().z;

				if (resultVertice.back().x > newBbox[1].x)
					newBbox[1].x = resultVertice.back().x;

				if (resultVertice.back().y > newBbox[1].y)
					newBbox[1].y = resultVertice.back().y;

				if (resultVertice.back().z > newBbox[1].z)
					newBbox[1].z = resultVertice.back().z;
			}
			else
			{
				newBbox.push_back(resultVertice.back());
				newBbox.push_back(resultVertice.back());
			}
		}
		if (newBigFiles.find(REPO_NODE_MESH_LABEL_VERTICES) != newBigFiles.end())
		{
			const uint64_t verticesByteCount = resultVertice.size() * sizeof(repo_vector_t);
			newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount);
			memcpy(newBigFiles[REPO_NODE_MESH_LABEL_VERTICES].second.data(), resultVertice.data(), verticesByteCount);
		}
		else
			builder.appendBinary(REPO_NODE_MESH_LABEL_VERTICES, resultVertice.data(), resultVertice.size() * sizeof(repo_vector_t));

		if (normals.size())
		{
			auto matInverse = invertMat(matrix);
			auto worldMat = transposeMat(matInverse);

			std::vector<repo_vector_t> resultNormals;
			resultNormals.reserve(normals.size());
			for (const repo_vector_t &v : normals)
			{
				auto transformedNormal = multiplyMatVecFake3x3(worldMat, v);
				normalize(transformedNormal);
				resultNormals.push_back(transformedNormal);
			}

			if (newBigFiles.find(REPO_NODE_MESH_LABEL_NORMALS) != newBigFiles.end())
			{
				const uint64_t byteCount = resultNormals.size() * sizeof(repo_vector_t);
				newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.resize(byteCount);
				memcpy(newBigFiles[REPO_NODE_MESH_LABEL_NORMALS].second.data(), resultNormals.data(), byteCount);
			}
			else
				builder.appendBinary(REPO_NODE_MESH_LABEL_NORMALS, resultNormals.data(), resultNormals.size() * sizeof(repo_vector_t));
		}

		RepoBSONBuilder arrayBuilder, outlineBuilder;
		for (size_t i = 0; i < newBbox.size(); ++i)
		{
			std::vector<float> boundVec = { newBbox[i].x, newBbox[i].y, newBbox[i].z };
			arrayBuilder.appendArray(std::to_string(i), boundVec);
		}

		if (newBbox[0].x > newBbox[1].x || newBbox[0].z > newBbox[1].z || newBbox[0].y > newBbox[1].y)
		{
			repoError << "New bounding box is incorrect!!!";
		}
		builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj());

		std::vector<float> outline0 = { newBbox[0].x, newBbox[0].y };
		std::vector<float> outline1 = { newBbox[1].x, newBbox[0].y };
		std::vector<float> outline2 = { newBbox[1].x, newBbox[1].y };
		std::vector<float> outline3 = { newBbox[0].x, newBbox[1].y };
		outlineBuilder.appendArray("0", outline0);
		outlineBuilder.appendArray("1", outline1);
		outlineBuilder.appendArray("2", outline2);
		outlineBuilder.appendArray("3", outline3);
		builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, outlineBuilder.obj());

		return MeshNode(builder.appendElementsUnique(*this), newBigFiles);
	}
	else
	{
		repoError << "Unable to apply transformation: Cannot find vertices within a mesh!";
		return  RepoNode(this->copy(), bigFiles);
	}
}
MeshNode RepoBSONFactory::makeMeshNode(
	const std::vector<repo_vector_t>                  &vertices,
	const std::vector<repo_face_t>                    &faces,
	const std::vector<repo_vector_t>                  &normals,
	const std::vector<std::vector<float>>             &boundingBox,
	const std::vector<std::vector<repo_vector2d_t>>   &uvChannels,
	const std::vector<repo_color4d_t>                 &colors,
	const std::vector<std::vector<float>>             &outline,
	const std::string                           &name,
	const int                                   &apiLevel)
{
	RepoBSONBuilder builder;
	uint64_t bytesize = 0; //track the (approximate) size to know when we need to offload to gridFS
	repoUUID uniqueID = generateUUID();
	auto defaults = appendDefaults(REPO_NODE_TYPE_MESH, apiLevel, generateUUID(), name, std::vector<repoUUID>(), uniqueID);
	bytesize += defaults.objsize();
	builder.appendElements(defaults);

	if (!vertices.size() || !faces.size())
	{
		repoWarning << "Creating a mesh (" << defaults.getUUIDField(REPO_NODE_LABEL_ID) << ") with no vertices/faces!";
	}
	std::unordered_map<std::string, std::pair<std::string, std::vector<uint8_t>>> binMapping;

	if (boundingBox.size() > 0)
	{
		RepoBSONBuilder arrayBuilder;

		for (int i = 0; i < boundingBox.size(); i++)
		{
			arrayBuilder.appendArray(std::to_string(i), boundingBox[i]);
			bytesize += boundingBox[i].size() * sizeof(boundingBox[i][0]);
		}

		builder.appendArray(REPO_NODE_MESH_LABEL_BOUNDING_BOX, arrayBuilder.obj());
	}

	if (outline.size() > 0)
	{
		RepoBSONBuilder arrayBuilder;

		for (int i = 0; i < outline.size(); i++)
		{
			arrayBuilder.appendArray(boost::lexical_cast<std::string>(i), outline[i]);
			bytesize += outline[i].size() * sizeof(outline[i][0]);
		}

		builder.appendArray(REPO_NODE_MESH_LABEL_OUTLINE, arrayBuilder.obj());
	}

	/*
		* TODO: because mongo has a stupid internal limit of 64MB, we can't store everything in a BSON
		* There are 2 options
		* 1. store binaries in memory outside of the bson and put it into GRIDFS at the point of commit
		* 2. leave mongo's bson, use our own/exteral library that doesn't have this limit and database handle this at the point of commit
		* below uses option 1, but ideally we should be doing option 2.
		*/

	if (vertices.size() > 0)
	{
		uint64_t verticesByteCount = vertices.size() * sizeof(vertices[0]);

		if (verticesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE)
		{
			std::string bName = UUIDtoString(uniqueID) + "_vertices";
			//inclusion of this binary exceeds the maximum, store separately
			binMapping[REPO_NODE_MESH_LABEL_VERTICES] =
				std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>());
			binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.resize(verticesByteCount); //uint8_t will ensure it is a byte addrressing
			memcpy(binMapping[REPO_NODE_MESH_LABEL_VERTICES].second.data(), &vertices[0], verticesByteCount);
			bytesize += sizeof(bName);
		}
		else
		{
			builder.appendBinary(
				REPO_NODE_MESH_LABEL_VERTICES,
				&vertices[0],
				vertices.size() * sizeof(vertices[0])
				);
			bytesize += verticesByteCount;
		}
	}

	if (faces.size() > 0)
	{
		builder << REPO_NODE_MESH_LABEL_FACES_COUNT << (uint32_t)(faces.size());

		// In API LEVEL 1, faces are stored as
		// [n1, v1, v2, ..., n2, v1, v2...]
		std::vector<repo_face_t>::iterator faceIt;

		std::vector<uint32_t> facesLevel1;
		for (auto &face : faces){
			auto nIndices = face.size();
			if (!nIndices)
			{
				repoWarning << "number of indices in this face is 0!";
			}
			facesLevel1.push_back(nIndices);
			for (uint32_t ind = 0; ind < nIndices; ind++)
			{
				facesLevel1.push_back(face[ind]);
			}
		}

		uint64_t facesByteCount = facesLevel1.size() * sizeof(facesLevel1[0]);

		if (facesByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE)
		{
			std::string bName = UUIDtoString(uniqueID) + "_faces";
			//inclusion of this binary exceeds the maximum, store separately
			binMapping[REPO_NODE_MESH_LABEL_FACES] =
				std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>());
			binMapping[REPO_NODE_MESH_LABEL_FACES].second.resize(facesByteCount); //uint8_t will ensure it is a byte addrressing
			memcpy(binMapping[REPO_NODE_MESH_LABEL_FACES].second.data(), &facesLevel1[0], facesByteCount);

			bytesize += sizeof(bName);
		}
		else
		{
			builder.appendBinary(
				REPO_NODE_MESH_LABEL_FACES,
				&facesLevel1[0],
				facesLevel1.size() * sizeof(facesLevel1[0])
				);

			bytesize += facesByteCount;
		}
	}

	if (normals.size() > 0)
	{
		uint64_t normalsByteCount = normals.size() * sizeof(normals[0]);

		if (normalsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE)
		{
			std::string bName = UUIDtoString(uniqueID) + "_normals";
			//inclusion of this binary exceeds the maximum, store separately
			binMapping[REPO_NODE_MESH_LABEL_NORMALS] =
				std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>());
			binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.resize(normalsByteCount); //uint8_t will ensure it is a byte addrressing
			memcpy(binMapping[REPO_NODE_MESH_LABEL_NORMALS].second.data(), &normals[0], normalsByteCount);

			bytesize += sizeof(bName);
		}
		else
		{
			builder.appendBinary(
				REPO_NODE_MESH_LABEL_NORMALS,
				&normals[0],
				normals.size() * sizeof(normals[0]));

			bytesize += normalsByteCount;
		}
	}

	//if (!vertexHash.empty())
	//{
	//	// TODO: Fix this call - needs to be fixed as int conversion is overloaded
	//	//builder << REPO_NODE_LABEL_SHA256 << (long unsigned int)(vertexHash);
	//}

	//--------------------------------------------------------------------------
	// Vertex colors
	if (colors.size())
	{
		uint64_t colorsByteCount = colors.size() * sizeof(colors[0]);

		if (colorsByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE)
		{
			std::string bName = UUIDtoString(uniqueID) + "_colors";
			//inclusion of this binary exceeds the maximum, store separately
			binMapping[REPO_NODE_MESH_LABEL_COLORS] =
				std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>());
			binMapping[REPO_NODE_MESH_LABEL_COLORS].second.resize(colorsByteCount); //uint8_t will ensure it is a byte addrressing
			memcpy(binMapping[REPO_NODE_MESH_LABEL_COLORS].second.data(), &colors[0], colorsByteCount);

			bytesize += sizeof(bName);
		}
		else
		{
			builder.appendBinary(
				REPO_NODE_MESH_LABEL_COLORS,
				&colors[0],
				colors.size() * sizeof(colors[0]));
			bytesize += colorsByteCount;
		}
	}

	//--------------------------------------------------------------------------
	// UV channels
	if (uvChannels.size() > 0)
	{
		// Could be unsigned __int64 if BSON had such construct (the closest is only __int64)
		builder << REPO_NODE_MESH_LABEL_UV_CHANNELS_COUNT << (uint32_t)(uvChannels.size());

		std::vector<repo_vector2d_t> concatenated;

		for (auto it = uvChannels.begin(); it != uvChannels.end(); ++it)
		{
			std::vector<repo_vector2d_t> channel = *it;

			std::vector<repo_vector2d_t>::iterator cit;
			for (cit = channel.begin(); cit != channel.end(); ++cit)
			{
				concatenated.push_back(*cit);
			}
		}

		uint64_t uvByteCount = concatenated.size() * sizeof(concatenated[0]);

		if (uvByteCount + bytesize >= REPO_BSON_MAX_BYTE_SIZE)
		{
			std::string bName = UUIDtoString(uniqueID) + "_uv";
			//inclusion of this binary exceeds the maximum, store separately
			binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS] =
				std::pair<std::string, std::vector<uint8_t>>(bName, std::vector<uint8_t>());
			binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.resize(uvByteCount); //uint8_t will ensure it is a byte addrressing
			memcpy(binMapping[REPO_NODE_MESH_LABEL_UV_CHANNELS].second.data(), &concatenated[0], uvByteCount);

			bytesize += sizeof(bName);
		}
		else
		{
			builder.appendBinary(
				REPO_NODE_MESH_LABEL_UV_CHANNELS,
				&concatenated[0],
				concatenated.size() * sizeof(concatenated[0]));

			bytesize += uvByteCount;
		}
	}

	return MeshNode(builder.obj(), binMapping);
}