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.