Custom FSManager - OpenMarshal/npm-WebDAV-Server GitHub Wiki
The purpose of a FSManager is to provide some functions which cannot be assigned to a resource. These fonctions are :
- Serialize and unserialize resources (for save states)
- Create a new resource
Full interface
interface FSManager
{
// Unique identifier for the instance of this FSManager
uid : string
// Serialize a managed resource
serialize(resource: any, obj: SerializedObject): object
// Unserialize a serialized resource
unserialize(data: any, obj: SerializedObject): IResource
// Create a new resource (called by the server for the methods : COPY, LOCK, MKCOL and PUT)
newResource(fullPath: string, name: string, type: ResourceType, parent: IResource): IResource
}
Unique identifier
The unique identifier MUST uniquely identify the way the FSManager instance works. For instance, all VirtualFSManager
do the same things, the same way, therefore, all VirtualFSManager
instances share the same unique identifier. If a new version of the VirtualFSManager
class is made and something might get in conflict with older version, then it needs a new unique identifier. But, if you create a new FSManager which, for instance, use a database, you must use a unique identifier for each database.
A good practice is to use the class name, followed by the version, and, if needed, followed by the serialized arguments.
Here is an example :
class PhysicalFSManager implements FSManager
{
uid : string = 'PhysicalFSManager_1.0.2';
// [...]
}
If, for example, the serialize
method is modified in a future version, but it will not be in conflict with the current version, then it's better to keep the actual uid
.
Here is another example :
class VirtualStoredFSManager implements FSManager
{
contentManager : IVirtualStoredContentManager;
uid : string;
constructor(contentManager : IVirtualStoredContentManager)
{
this.contentManager = contentManager;
this.uid = 'VirtualStoredFSManager_1.3.3_' + contentManager.uid;
}
// [...]
}
Because all VirtualStoredFSManager
will not provide the same management, it is distinguished by the argument unique identifier. In this case, the argument has an identifier, but you can use an addon identifier as argument to delegate the instance dissociation to the creator of the instance :
class VirtualStoredFSManager implements FSManager
{
contentManager : IVirtualStoredContentManager;
uid : string;
constructor(uid : string, contentManager : IVirtualStoredContentManager)
{
this.contentManager = contentManager;
this.uid = 'VirtualStoredFSManager_1.3.3_' + uid;
}
// [...]
}
This way, you can keep the same uid
even if you use arguments in your constructor which can change in the future.
Serialization / Unserialization
When the server's save
and load
methods are called, the serialization and the unserialization will be delegated to the FSManager stored in each resource (the fsManager
property). The serialize
method returns an object which represents the resource asked to be serialized and which contains only what the system needs to instantiate the resource again at the current state (except for the locks, which MUST NOT be serialized). For more information about what to serialize, look at the Persistence page.
When saving, the server creates a SerializedObject
instance.
class SerializedObject
{
data : any
type : ResourceType
children : SerializedObject[]
managerUID : string
constructor(managerUID : string, type : ResourceType)
{
this.managerUID = managerUID;
this.children = [];
this.type = type;
}
}
This instance will be passed to the serialize
and the unserialize
methods of the FSManager, but they MUST NOT be modified.
You can find inside :
Property name | Type | Description
-|-
managerUID
| string
| The uid
of the FSManager instance referenced by the fsManager
property in the resource.
data
| any
| The result of the serialize
method.
type
| ResourceType
| The type of the resource, provided by the type
method of the resource.
children
| SerializedObject[]
| Children of the resource.
When serializing, the data
and children
properties will be empty (because not computed yet).
When unserializing, all properties will be filled.
Here is an example (VirtualFSManager
) of the serialize
and unserialize
methods :
serialize(resource : any, obj : SerializedObject) : object
{
const result : any = {
dateCreation: resource.dateCreation,
dateLastModified: resource.dateLastModified,
properties: resource.properties
};
result.name = resource.name;
if(resource.content)
{
result.content = resource.content;
result.len = resource.len;
}
return result;
}
unserialize(data : any, obj : SerializedObject) : IResource
{
if(obj.type.isDirectory)
{
const rs = new VirtualFolder(data.name, null, this);
rs.dateCreation = data.dateCreation;
rs.dateLastModified = data.dateLastModified;
rs.properties = data.properties;
return rs;
}
if(obj.type.isFile)
{
const rs = new VirtualFile(data.name, null, this);
if(data.content)
{
rs.content = data.content.map((a) => new Buffer(a));
rs.len = data.len;
}
rs.dateCreation = data.dateCreation;
rs.dateLastModified = data.dateLastModified;
rs.properties = data.properties;
return rs;
}
throw Errors.UnrecognizedResource;
}
Creating a new resource
When a resource creation is needed, the server calls the newResource
method of the parent resource. For instance, if a MKCOL request is made with a path /data/subdata/newFolder
, then the newResource
method of the FSManager of the /data/subdata
resource will be called with arguments : fullPath='/data/subdata/newFolder' ; name='newFolder' ; type=ResourceType.Directory ; parent=parentResource
.
This method must only instantiate a new managed resource with the right parent and the right FSManager instance and throw an error if a problem occurs.
Here is an example with the VirtualFSManager
:
newResource(fullPath : string, name : string, type : ResourceType, parent : IResource) : IResource
{
if(type.isDirectory)
return new VirtualFolder(name, parent, this);
if(type.isFile)
return new VirtualFile(name, parent, this);
throw Errors.UnrecognizedResource;
}
The create
resource's method will be called by the server and MUST NOT be called by the FSManager.
Find full examples
If you want to see full examples, you can find some in the manager folder of the project.