Spaces:
Sleeping
Extensions - Implementing Drivers
Puter's concept of drivers has existed long before the extension system was refined, and to keep things moving forward it has become easier to develop Puter drivers in extensions than anywhere else in Puter's source. If you want to build a driver, an extension is the recommended way to do it.
What are Puter drivers?
Puter drivers are all called through the /drivers/call endpoint, so they
can be thought of as being "above" the HTTP layer. When a method on a driver
throws an error you will still receive a 200 HTTP status response because
the the invocation - from the HTTP layer - was successful.
A driver response follows this structure:
{
"success": true,
"service": {
"name": "implementation-name"
},
"result": "any type of value goes here",
"metadata": {}
}
There exists an example driver called hello-world. This driver implements
a method called greet with the optional parameter subject which returns
a string greeting either World (default) or the specified subject.
await puter.call('hello-world', 'no-frills', 'greet', { subject: 'Dave' });
Let's break it down:
'hello-world'
'hello-world' is the name of an "interface". An interface can be thought of
a contract of what inputs are allowed and what outputs are expected. For
example the hello-world interface specifies that there must be a method
called greet and it should return a string representing a greeting.
To add another example, an interface called weather specify a method called
forcast5day that always returns a list of 5 objects with a particular
structure.
no-frills
'no-frills' is a simple - "no frills" (nothing extra) - implementation of
the hello-world interface. All it does is return the string:
`Hello, ${subject ?? 'World'}!`
'greet'
greet is the method being called. It's the only method on the hello-world
interface.
{ subject: 'Dave' }
These are the arguments to the greet method. The arguments specify that we
want to say "Hello" to Dave. Hopefully he doesn't ask us to open the pod bay
doors, or if he does we hopefully have extensions to add a driver interface
and driver implementation for the pod bay doors so that we can interact with
them.
Drivers in Extensions
The hellodriver extension adds the hello-world interface like this:
extension.on('create.interfaces', event => {
// createInterface is the only method on this `event`
event.createInterface('hello-world', {
description: 'Provides methods for generating greetings',
methods: {
greet: {
description: 'Returns a greeting',
parameters: {
subject: {
type: 'string',
optional: true
},
locale: {
type: 'string',
optional: true
},
}
}
}
})
});
The hellodriver extension adds the no-frills implementation for
hello-world like this:
extension.on('create.drivers', event => {
event.createDriver('hello-world', 'no-frills', {
greet ({ subject }) {
return `Hello, ${subject ?? 'World'}!`;
}
});
});`
You can pass an instance of a class for a driver implementation as well:
class Greeter {
greet ({ subject }) {
return `Hello, ${subject ?? 'World'}!`;
}
}
extension.on('create.drivers', event => {
event.createDriver('hello-world', 'no-frills', new Greeter());
});`
Instances of classes being supported
may seem to be implied by the example before this
one, but that is not the case. What's shown here is that function members
of the object passed to createDriver will not be "bound" (have their
.bind() method called with a different object as the instance variable).
Permission Denied
When you try to access a driver as any user other than the default
admin user, it will not work unless permission has been granted.
The hellodriver extension grants permission to all clients using
the following snippet:
extension.on('create.permissions', event => {
event.grant_to_everyone('service:no-frills:ii:hello-world');
});
The create.permissions event's event object has a few methods
you can use depending on the desired granularity:
grant_to_everyone- grants permission to all usersgrant_to_users- grants permission to only registered users (i.e. not to temporary/guest users)