-
Notifications
You must be signed in to change notification settings - Fork 68
Custom resources
The custom resource MUST implement the IResource
interface :
interface IResource
{
parent : IResource
fsManager : FSManager
// ****************************** Actions ****************************** //
create(callback : SimpleCallback)
delete(callback : SimpleCallback)
moveTo(parent : IResource, newName : string, overwrite : boolean, callback : SimpleCallback)
rename(newName : string, callback : Return2Callback<string, string>)
// ****************************** Content ****************************** //
write(targetSource : boolean, callback : ReturnCallback<Writable>)
read(targetSource : boolean, callback : ReturnCallback<Readable>)
mimeType(targetSource : boolean, callback : ReturnCallback<string>)
size(targetSource : boolean, callback : ReturnCallback<number>)
// ****************************** Locks ****************************** //
getLocks(callback : ReturnCallback<Lock[]>)
setLock(lock : Lock, callback : SimpleCallback)
removeLock(uuid : string, callback : ReturnCallback<boolean>)
getAvailableLocks(callback : ReturnCallback<LockKind[]>)
getLock(uuid : string, callback : ReturnCallback<Lock>)
// ****************************** Children ****************************** //
addChild(resource : IResource, callback : SimpleCallback)
removeChild(resource : IResource, callback : SimpleCallback)
getChildren(callback : ReturnCallback<IResource[]>)
// ****************************** Properties ****************************** //
setProperty(name : string, value : ResourcePropertyValue, callback : SimpleCallback)
getProperty(name : string, callback : ReturnCallback<ResourcePropertyValue>)
removeProperty(name : string, callback : SimpleCallback)
getProperties(callback : ReturnCallback<object>)
// ****************************** Std meta-data ****************************** //
creationDate(callback : ReturnCallback<number>)
lastModifiedDate(callback : ReturnCallback<number>)
webName(callback : ReturnCallback<string>)
displayName?(callback : ReturnCallback<string>)
type(callback : ReturnCallback<ResourceType>)
// ****************************** Gateway ****************************** //
gateway?(arg : MethodCallArgs, path : FSPath, callback : (error : Error, resource ?: IResource) => void)
}
interface IResource
{
parent : IResource
// [...]
}
This property is used :
- in the case of a
MOVE
, to define if the resource must be moved or just renamed. - in the case of any request, to define if the resource is locked by a parent resource.
- to define if the resource is the root resource of the server.
If the resource is the root resource of the server (no parent), then the parent
property must be set to null
. If the resource is not the root resource of the server, then the parent
property must be set to the reference to the parent resource (of type IResource
). When the resource is moved to another parent, the parent
property must be modified.
interface IResource
{
fsManager : FSManager
// [...]
}
This property is used :
- in the case of a serialization, to serialize a link to the fsManager for future unserialization.
- in the case of a unserialization, to define which method to call to rebuild the serialized resource.
- in the case of a
LOCK
,PUT
,MKCOL
involving a resource creation, to define which method to call to create a new child resource of a specific type. - to define if two resources are in the same file system (for instance, if two resources are managed by the same FSManager).
- to provide to some resources a centralized management of common resources (database connection, folder to store the content, etc...).
- to test if an object is of type
IResource
.
This property must not be null
or undefined
. If changed, this will alter the behavior of the child creation and the persistence of the resource.
interface IResource
{
create(callback : SimpleCallback)
// [...]
}
This method is called :
- on a newly created resource (not when unserialized), in the case of
PUT
,COPY
andLOCK
involving a resource creation.
This method must be used to do some jobs when the resource is created. For instance, a PhysicalFile
will create the file on the file system when the create
method is called by the server. This method is called once in the life of the resource. If the server didn't created the resource, the developer must make sure to invoke it if needed. This method can be "empty" (just call the callback) if nothing is needed to be done.
interface IResource
{
delete(callback : SimpleCallback)
// [...]
}
This method is called :
- when the resource is irremediably removed from the server, in the case of a
DELETE
,MOVE
(when overwritten).
This method must be used to do some jobs when the resource is deleted. This method is called once in the life of the resource. This method is NOT called when the server serialized the resources and closed. If the resource is removed by another entity than the server, the delete
method must be called to ensure the jobs to be done.
When successful, this method must, at least, move itself from its parent (there is a helper static method to do so : StandardResource.removeFromParent(...)
).
interface IResource
{
moveTo(parent : IResource, newName : string, overwrite : boolean, callback : SimpleCallback)
// [...]
}
This method is called :
- in the case of a
MOVE
.
This method must handle the move of the resource into the parent
resource and rename itself with the newName
.
The resource must be removed from the old parent and added to the new parent.
The resource must be renamed (if needed).
The resource must NOT overwrite/delete a conflicting resource if the overwrite
parameter is equals to false
.
The resource must call the callback
with the error Errors.ResourceAlreadyExists
if an overwrite was needed but it was not allowed.
This is a lot to handle for a little method, but it can use some help from the StandardResource.standardMoveTo(...)
static method. This method will move by copy the resource, involving the creation of a new resource and the deletion of the old one (and manage overwrite if needed). This behaviour is intended to fit most of situations.
interface IResource
{
rename(newName : string, callback : Return2Callback<string, string>)
// [...]
}
This method is never used by the server itself, but it allow faster computation of moving a resource when its parent doesn't change.
The implementation of this method doesn't need to care about the conflicts. It must alter the resource name and, optionally, do some jobs (for instance, a PhysicalFile
will also rename the file it is pointing to and update its real path).
interface IResource
{
write(targetSource : boolean, callback : ReturnCallback<Writable>)
read(targetSource : boolean, callback : ReturnCallback<Readable>)
// [...]
}
These methods provide a stream to read or write into the file. The targetSource
parameter allow to switch from the source of the content to the computed version of the content. If there is no computed version of the content or if the source of the content is not accessible (or not handled) then the targetSource
parameter must be ignore and provide whatever content is possible.
interface IResource
{
mimeType(targetSource : boolean, callback : ReturnCallback<string>)
// [...]
}
This method is used :
- in the case of a
PROPFIND
,GET
andHEAD
.
It is the to provide the mime-type of the content and, if possible, its charset. The resulting string must be a valid value for the Content-Type
HTTP header.
Read the write/read section to find more information about the targetSource
parameter.
interface IResource
{
size(targetSource : boolean, callback : ReturnCallback<number>)
// [...]
}
This method is used :
- in the case of a
PROPFIND
,GET
andHEAD
.
The resulting value must be the size of the content of the resource.
Read the write/read section to find more information about the targetSource
parameter.
interface IResource
{
getLocks(callback : ReturnCallback<Lock[]>)
setLock(lock : Lock, callback : SimpleCallback)
removeLock(uuid : string, callback : ReturnCallback<boolean>)
getLock(uuid : string, callback : ReturnCallback<Lock>)
// [...]
}
These methods allow to manage and display locks of a resource. It must affect/scan only the locks of the current resource. It must not be spread to the parent or the children.
interface IResource
{
getAvailableLocks(callback : ReturnCallback<LockKind[]>)
// [...]
}
This method is used :
- in the case of a
PROPFIND
.
This method allow tell which locks are supported by the resource.
Here is the current standard implementation :
getAvailableLocks(callback : ReturnCallback<LockKind[]>)
{
callback(null, [
new LockKind(LockScope.Exclusive, LockType.Write),
new LockKind(LockScope.Shared, LockType.Write)
])
}
interface IResource
{
addChild(resource : IResource, callback : SimpleCallback)
removeChild(resource : IResource, callback : SimpleCallback)
getChildren(callback : ReturnCallback<IResource[]>)
// [...]
}
These methods allow to manage the children of the resource. These methods must affect the parent
property if needed.
interface IResource
{
setProperty(name : string, value : ResourcePropertyValue, callback : SimpleCallback)
getProperty(name : string, callback : ReturnCallback<ResourcePropertyValue>)
removeProperty(name : string, callback : SimpleCallback)
getProperties(callback : ReturnCallback<object>)
// [...]
}
Allow to manage/list the properties of the resource.
Here is the ResourcePropertyValue
definition :
interface XMLElement
{
declaration ?: any
attributes ?: any
elements : XMLElement[]
name ?: string
}
type ResourcePropertyValue = string | XMLElement | XMLElement[]
interface IResource
{
creationDate(callback : ReturnCallback<number>)
lastModifiedDate(callback : ReturnCallback<number>)
// [...]
}
The resulting value of theses methods must be the milliseconds elapsed since the UNIX epoch.
You can use the Date.now()
method to get this value at the current time, store it in the instance of the class and call the callbacks of these methods with the stored value.
interface IResource
{
webName(callback : ReturnCallback<string>)
// [...]
}
This method provides the unique name of the resource among the children of its parent. This name is used for the url of the resource and, therefore, must match the HTTP url constraints.
interface IResource
{
displayName?(callback : ReturnCallback<string>)
// [...]
}
This method is optional. If you don't define this method, the webName
method will be used instead. This method is used as content of the DAV:displayname
XML element in the PROPFIND
response.
There is no constraint about the text used and no unicity required. As expressed in the RFC4918, the result must describe the resource.
interface IResource
{
type(callback : ReturnCallback<ResourceType>)
// [...]
}
This method returns the type of the resource.
For more information about the type of a resource, take a look at here.
interface IResource
{
gateway?(arg : MethodCallArgs, path : FSPath, callback : (error : Error, resource ?: IResource) => void)
// [...]
}
This method is optional but when it is provided, it will tell the server to delegate its resource search based on the path to this method.
This way, if the server looks for the resource at /folder1/folder2/folder3/file.txt
and the resource at /folder1/folder2
implements the gateway
method, then the server will stop at /folder1/folder2
and ask call the resource's gateway
method to provide the resource at /folder3/file.txt
.
Because the arg : MethodCallArgs
is provided, this method can provide a different resource tree depending on the user connected, the headers, etc...
You might find more information at the dedicated page.
You can easily test a large portion of your custom resource thanks to the ResourceTester
class.
Here is a little sample of the code to use :
new webdav.ResourceTester({
canHaveVirtualFolderChildren: false,
canHaveVirtualFileChildren: false,
canGetLastModifiedDate: true,
canGetCreationDate: true,
canRemoveChildren: false,
canHaveChildren: false,
canGetChildren: false,
canGetMimeType: true,
canBeCreated: true,
canBeDeleted: true,
canBeRenamed: true,
canGetSize: true,
canBeMoved: true,
canWrite: true,
canRead: true,
canLock: true
},
// For each battery of tests, create the resource to test
// willCreate : A value of true means you must not call the '.create(...)' method because it will be tested
(willCreate, cb) => cb(new webdav.VirtualFile('test'))
).run((results) => {
// Display the results of the tests
console.log(results.all.isValid);
if(results.all.errors)
for(const value of results.all.errors)
console.log(value.toString());
});
Here is an example using the willCreate
:
new webdav.ResourceTester({
canHaveVirtualFolderChildren: true,
canHaveVirtualFileChildren: true,
canGetLastModifiedDate: true,
canGetCreationDate: true,
canRemoveChildren: true,
canHaveChildren: true,
canGetChildren: true,
canGetMimeType: false,
canBeCreated: true,
canBeDeleted: true,
canBeRenamed: true,
canGetSize: false,
canBeMoved: true,
canWrite: false,
canRead: false
},
(willCreate, cb) => {
const name = path.join(rootFolder, 'testFolder' + (++fid2).toString());
if(!willCreate)
{
fs.mkdir(name, () => {
cb(new webdav.PhysicalFolder(name))
})
}
else
cb(new webdav.PhysicalFolder(name))
}
).run((results) => {
console.log(results.all.isValid);
if(results.all.errors)
for(const value of results.all.errors)
console.log(value.toString());
})
- Home
- Version 2
- Install
- Quick Start Guide
- Examples
- Concepts
- User concepts
- Server concepts
- Customizing
- Version 1 [Obsolete]
- Install
- Quick Start Guide
- Examples
- Features
- Resource concepts
- User concepts
- Server concepts
- Customizing
- Project