Data Models

The data models used to implement the Storage module within the Cosync Engine include the following two classes.

  • CosyncAssetUpload
  • CosyncAsset

These models assume a Realm partition key called _partition.

CosyncAssetUpload

The CosyncAssetUpload object is used by a client application to access storage that is managed by the server in the bucket storage. Amazon S3 bucket storage is used for heavy objects such as images, videos, audio files, or regular files. The developer configures the server with the appropriate credentials to access the bucket storage. These credentials are never kept by a client device for security purposes. The storage upload object exists for the purpose of facilitating communication between a client device and the bucket storage for the application. It used by the client to upload URLs to upload the asset to the bucket storage.

The CosyncAssetUpload object is used by a Cosync application to model the life-cycle of uploading an asset to the Amazon S3 storage, and creating a CosyncAsset that records it. The life-cycle is regulated by the value of the status property, which can have the following values

  • pending
  • initialized
  • error
  • uploaded
  • completed

The purpose of the CosyncAssetUpload object is to facilitate communication between a client device and Amazon S3 storage. The credentials for Amazon S3 are never kept by a client device for security purposes.

status = pending (set by client)

To initiate upload of an asset to Amazon S3 storage, the client application code will create a CosyncAssetUpload object and set the status property to pending. In response to the newly created CosyncAssetUpload object, a MongoDB Realm Application server trigger will compute a set of writeUrl(s) for the client to upload the asset with. These writeUrl(s) are pre-signed URLs to the Amazon S3 storage bucket. The pending CosyncAssetUpload object should have the following properties set:

  • status (‘pending’)
  • assetPartition
  • filePath
  • contentType
  • uid
  • sessionId
  • extra

The MongoDB Realm Application server side code will listen for new CosyncAssetUpload objects being created and service them. A path is used by the Amazon S3 Service to keep track of an asset that is uploaded to it. This path is a unique path within the storage bucket, it contains a file path name, a time stamp, and a file extension. The new asset will always but put underneath a root path for the user Id that is uploading the asset. The path is computed from an input filePath and the uid that are passed by the client. The input filePath has the following format:

	filePath := <path>/<file-name>.<extension>

The client side application also needs to set the Realm user Id in the uid property of the new CosyncAssetUpload object. The sessionId property uniquely identifies the device from which the new CosyncAssetUpload object is uploaded from - it must be the same value for that user on that device always. The extra property is used by the uploading client application code to store extra information during the life-cycle of the CosyncAssetUpload object - this can be platform specific. The contentType property specifies the content type of the asset. The various values for contentType are listed in the Content Types table below.

When all of these properties have been set, the client application code should write the CosyncAssetUpload object to Realm in order to trigger a response.

status = initialized (set by backend)

Once these initial properties are set and the CosyncAssetUpload object is written to Realm, the backend MongoDB Application triggers functions will service the upload request. As part of this process, the backend will also compute a number of writeUrl(s) that will be used by the client application code to upload the asset with. These writeUrl(s) are pre-signed URLs that will enable to the client to upload the asset using an HTTPS PUT command. Depending on the contentType of the asset.

The asset path that is computed by the backend MongoDB Realm trigger is stored in the path property of the CosyncAssetUpload object. This path property has the following format.

	path := [public/]<path>/<file-name>-<time stamp>.<extension>

If the asset is non-expiring, the path will be prefixed by the ‘public/’ directory name. For a specific input filePath, the backend will add a time stamp to the path to ensure that no file collisions take place. The computed path with a time stamp will be saved in the path property.

During the servicing of the pending CosyncAssetUpload object, the backend may generate up to 4 write URLs:

  • writeUrl
  • writeUrlSmall
  • writeUrlMedium
  • writeUrlLarge
  • writeUrlVideoPreview

The original asset should always be uploaded to writeUrl. If the asset is an image or a video, there will also be image cuts that will need to be uploaded. These cuts are small (300 pixels), medium (600 pixels), and large (900 pixels). For video assets, a preview image will be uploaded to writeUrlVideoPreview. As part of the process of servicing a pending asset, the backend will set create these writeURLs and set the status property to initialized. For a video asset, cuts for the video preview will also be stored in the small, medium, and large URLs respectively.

When the backend end has finished computing the various writeUrl(s) needed to upload the asset and written them to the CosyncAssetUpload object, it will set the status property to initialized. The newly created CosyncAsset object will have a status property set pending and the same value for _id of the CosyncAssetUpload object.

status = uploaded (set by client)

The client will respond to the initialized CosyncAssetUpload object by uploading the asset data to the writeUrl(s) that were set in the previous step. At this time, the writeUrl(s) only support an HTTPS PUT command, and do not support multi-part image upload. However, progress status is completely available on a PUT command.

As part of the client side servicing of the upload process, the client application code must also set various meta-data properties associated with the asset, including

  • size (size in bytes of asset)
  • duration (duration of video or audio asset in seconds)
  • color (background color of video or image asset #rrggbb format
  • xRes (x resolution of image or video asset)
  • yRes (y resolution of image or video asset)
  • containerId (optional container id that asset belongs to)
  • caption (caption text for asset)

When the upload of the asset is complete, the client side code will set the status property to uploaded and write the CosyncAssetUpload object back to Realm.

status = completed (set by backend)

The server will respond to the uploaded status change on the CosyncAssetUpload object by creating a corresponding CosyncAsset object in the partition specified by the assetPartition property. If assetPartition is not specified, the CosyncAsset is created in the same partition as the CosyncAssetUpload object. When the CosyncAsset object is created, the backend will set the status property in the CosyncAssetUpload object to completed. Also, the backend will change the status property of the associated asset to active. At this point, the upload process is considered done.

CosyncAssetUpload = {
    "primaryKey":"_id",
	"properties": {
		"_id":{ "type": "string", "indexed": true },
		
		/* partition */
		"_partition":{ "type": "string", "indexed": true },
		
		/* uid of user that created the storage upload */
		"uid": { "type": "string", "indexed": true },
		
		/* session Id of device or web app that created the storage upload */
		"sessionId": { "type": "string", "indexed": true },
	
		/* extra information put in by client (only used by client) */
		"extra": { "type": "string", "default": "" },
		
		/* asset partition */
		"assetPartition":{ "type": "string", "default": "". "indexed": true },
		
		/* input file path */
		"filePath": { "type": "string", "default": "" },
		
		/* path of file in bucket (storage path key) */
		"path": { "type": "string", "default": "" },	
			
		/* expiration hours */
		"expirationHours": { "type": "number", "default": 24},
		
		/* content type (set by client) */
		"contentType": { "type": "string", "optional": true },

		/* size in bytes of asset */
		"size": { "type": "int", "indexed": true, "optional": true },
		
		/* duration for audio or video file in seconds */
		"duration": {"type": "int", "optional": true},
		
		/* average color (hex color of background for image) */
		"color": { "type": "string", "default":"#000000" },
		
		/* x resolution of original image or video asset */
		"xRes": { "type": "int", "default":0 },

		/* y resolution of original image or video asset */
		"yRes": { "type": "int", "default":0 },
		
		/* container id - if asset belongs to a container */
		"containerId": { "type": "string", "indexed": true, "optional": true },
		
		/* caption */
		"caption": { "type": "string", "default": "" },

		/* write Url (filled in by server) */
		"writeUrl": { "type": "string", "optional": true },
		
		/* write Url Small (filled in by server) */
		"writeUrlSmall": { "type": "string", "optional": true },

		/* write Url Medium (filled in by server) */
		"writeUrlMedium": { "type": "string", "optional": true },

		/* write Url Large (filled in by server) */
		"writeUrlLarge": { "type": "string", "optional": true },

		/* write Url Video Preview (filled in by server) */
		"writeUrlVideoPreview": { "type": "string", "optional": true },

		/* status */
		"status": { "type": "string", "indexed": true, "default": "pending" },

		"createdAt": { "type": "date", "optional": true },
		"updatedAt": { "type": "date", "optional": true },
	}
}

CosyncAsset

An CosyncAsset object records an asset that has been uploaded to bucket storage using a CosyncAssetUpload object. The CosyncAsset is created by the server.

Assets are used for storing any file data to bucket storage. The initial bucket storage used by Cosync is Amazon S3. The path of the asset is taken from the CosyncAssetUpload object after the asset has been upload to the bucket storage, by setting its status property to uploaded. This path will include the time stamp that was set by the server. Furthermore, this path is sufficient to determine the full paths of all associated cuts for image assets and video assets.

If the asset type is an image, there should be cuts for different sizes for the image - small, medium, and large. All the cuts are generated by the client and uploaded to the bucket storage through the CosyncAssetUpload object. The cuts always preserve the aspect ratio of the original image. The largest dimension of the image is scaled to fit within the size of the image cut (300, 600, or 900). The smaller dimension is always smaller than the size of the image cut. If the largest dimension is smaller than the size of the cut, the cut will be the same as the original image - cuts are never scaled up. In the case of a video asset, there is also a video preview image along with cuts for the video preview - small, medium, and large.

The status property is used to record the status of the asset. The choices are

  • pending
  • active
  • archived

The active is the default, which means that the asset is actively used. The status field can be set to archived, which means that the asset is not available to use - but still has associated bucket storage that holds the actual asset. To delete the asset permanently from Amazon S3 Storage, the client application must call the CosyncRemoveAsset function.

The CosyncAsset object also contains an optional container id that allows the developer to query assets that belong to a specific container, e.g. a chat thread, or an appliance, or anything else. This is saved in the containerId property. The container id is specified by the CosyncAssetUpload object during the upload process.

An asset can either expire or not. Non expiring assets will have an expirationHours property set to a positive value greater than zero, that denotes the expiration in hours. If expirationHours is set to zero or not present, the asset does not expire and is considered public. The associated url(s) for a non-expiring asset are always valid, and never need to be refreshed. The associated url(s) for an expiring asset will not be valid after the current date is greater than the date set by the expiration property. In order to access the url(s) for an expired asset, the client will have to request an asset refresh through a server function call. The expirationHours property is copied from the CosyncAssetUpload object at the time the CosyncAsset object is created.

CosyncAsset = {
    "primaryKey":"_id",
	"properties": {
		"_id":{ "type": "string", "indexed": true },
		
		/* partition */
		"_partition":{ "type": "string", "indexed": true },
		
		/* uid of user that created the asset */
		"uid": { "type": "string", "indexed": true },
		
		/* path of the file in bucket (storage path key) */
		"path": { "type": "string", "default": "" },
		
		/* expiration hours */
		"expirationHours": { "type": "number", "optional": true}

		/* expiration */
		"expiration": { "type": "date", "optional": true, "indexed": true  },
		
		/* content type */
		"contentType": { "type": "string", "indexed": true },
		
		/* caption */
		"caption": { "type": "string", "default": "" },
		
		/* size in bytes of asset */
		"size": { "type": "int", "indexed": true, "optional": true },
		
		/* duration for audio or video file in seconds */
		"duration": {"type": "int", "optional": true},
		
		/* average color (hex color of background for image) */
		"color": { "type": "string", "default":"#000000" },
		
		/* x resolution of original image or video asset */
		"xRes": { "type": "int", "default":0 },

		/* y resolution of original image or video asset */
		"yRes": { "type": "int", "default":0 },
		
		/* container id - if asset belongs to a container */
		"containerId": { "type": "string", "indexed": true, "optional": true },

		/* status */
		"status":{ "type": "string", "indexed": true, "default": "active" },
		
		/* url of asset in storage (only if expires == false) */
		"url": { "type": "string", "optional": true },

		/* 300 pixels in largest dimension (only if expires == false) */
		"urlSmall": { "type": "string", "optional": true },
		
		/* 600 pixels in largest dimension (only if expires == false) */
		"urlMedium": { "type": "string", "optional": true },
		
		/* 900 pixels in largest dimension (only if expires == false) */
		"urlLarge": { "type": "string", "optional": true },
		
		/* video preview (only if expires == false) */
		"urlVideoPreview": { "type": "string", "optional": true },

		"createdAt": { "type": "date", "optional": true },
		"updatedAt": { "type": "date", "optional": true },
	}
}

Content Types


	/* text asset type */
    "text/plain"
    "text/css"
    "text/richtext"
    "text/html"
    "text/xml"
    "text/tab-separated-values"
    "text/comma-separated-values"
    "text/x-vcard"
    "text/csv"
    "text/rtf"

	/* image asset type */
    "image/bmp"
    "image/gif"
    "image/jpeg"
    "image/png"
    "image/tiff"
    "image/ico"
    "image/vnd.adobe.photoshop"
    
	/* video asset type */
    "video/x-msvideo"
    "video/mpeg"
    "video/quicktime"
    "video/x-sgi-movie"
    "video/mp4"

	/* audio asset type */
    "audio/x-aiff"
    "audio/mpeg"
    "audio/x-pn-realaudio"
    "audio/basic"
    "audio/mid"
    "audio/x-wav"
    "audio/mp3"

	/* file asset type */
    "application/octet-stream"
    "application/msword"
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
    "application/vnd.ms-powerpoint"
    "application/vnd.openxmlformats-officedocument.presentationml.presentation"
    "application/vnd.openxmlformats-officedocument.presentationml.template"
    "application/vnd.openxmlformats-officedocument.presentationml.slideshow"
    "application/vnd.ms-excel"
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    "application/vnd.openxmlformats-officedocument.spreadsheetml.template"
    "application/vnd.ms-outlook"
    "application/vnd.ms-project"
    "application/x-msdownload"
    "application/zip"
    "application/postscript"
    "application/xml"
    "application/pdf"
    "application/x-dvi"
    "application/x-gtar"
    "application/x-tar"
    "application/x-gzip"
    "application/x-compress"
    "application/x-compressed"
    "application/illustrator"
    "application/x-shockwave-flash"
    "application/x-iwork-keynote-sffkey"
    "application/x-iwork-pages-sffpages"
    "application/x-iwork-numbers-sffnumbers"

functions

CosyncRefreshAsset

The function CosyncRefreshAsset() is called by a client to refresh the URLs of an expiring asset. If called, the function will recompute the URLs of an expired asset and update the CosyncAsset object with the updated url(s). It will also modify the updatedAt property. The only parameter to this function is the assetId.

exports = async function(assetId) {
};
CosyncRemoveAsset

The function CosyncRemoveAsset() is called by a client to remove the asset from MongoDB Realm and from the Amazon S3 Storage. This function will only work if the user Id of that calls the function is equal to the uid of the CosyncAsset object.

exports = async function(assetId) {
};