Bläddra i källkod

dev: begin adding new driver call method

KernelDeimos 10 månader sedan
förälder
incheckning
d5ec40078f

+ 7 - 1
src/backend/src/api/APIError.js

@@ -321,7 +321,13 @@ module.exports = class APIError {
         },
         'no_implementation_available': {
             status: 502,
-            message: ({ interface_name }) => `No implementation available for interface ${quot(interface_name)}`,
+            message: ({
+                iface,
+                interface_name,
+                driver
+            }) => `No implementation available for ` +
+                (iface ?? interface_name) ? 'interface' : 'driver' +
+                ' ' + quot(iface ?? interface_name ?? driver) + '.',
         },
         'method_not_found': {
             status: 404,

+ 2 - 0
src/backend/src/data/hardcoded-permissions.js

@@ -65,6 +65,7 @@ const implicit_user_app_permissions = [
 const hardcoded_user_group_permissions = {
     system: {
         'b7220104-7905-4985-b996-649fdcdb3c8f': {
+            'service:helloworld:ii:helloworld': {},
             'driver:puter-kvstore': {
                 $: 'json-address',
                 path: '/admin/.policy/drivers.json',
@@ -87,6 +88,7 @@ const hardcoded_user_group_permissions = {
             },
         },
         '78b1b1dd-c959-44d2-b02c-8735671f9997': {
+            'service:helloworld:ii:helloworld': {},
             'driver:puter-kvstore': {
                 $: 'json-address',
                 path: '/admin/.policy/drivers.json',

+ 6 - 2
src/backend/src/routers/drivers/call.js

@@ -58,7 +58,7 @@ module.exports = eggspress('/drivers/call', {
     const interface_name = req.body.interface;
     const test_mode = req.body.test_mode;
 
-    const params = req.headers['content-type'].includes('multipart/form-data')
+    const args = req.headers['content-type'].includes('multipart/form-data')
         ? await _handle_multipart(req)
         : req.body.args;
 
@@ -66,7 +66,11 @@ module.exports = eggspress('/drivers/call', {
     if ( test_mode ) context = context.sub({ test_mode: true });
 
     const result = await context.arun(async () => {
-        return await svc_driver.call(interface_name, req.body.method, params);
+        return await svc_driver.call({
+            iface: interface_name,
+            method: req.body.method,
+            args
+        });
     });
 
     _respond(res, result);

+ 5 - 2
src/backend/src/routers/kvstore/getItem.js

@@ -60,8 +60,11 @@ router.post('/getItem', auth, express.json(), async (req, res, next)=>{
     const svc_driver = Context.get('services').get('driver');
     let driver_result;
     try {
-        const driver_response = await svc_driver.call(
-            'puter-kvstore', 'get', { key: req.body.key });
+        const driver_response = await svc_driver.call({
+            iface: 'puter-kvstore',
+            method: 'get',
+            args: { key: req.body.key },
+        });
         if ( ! driver_response.success ) {
             throw new Error(driver_response.error?.message ?? 'Unknown error');
         }

+ 6 - 3
src/backend/src/routers/kvstore/setItem.js

@@ -72,11 +72,14 @@ router.post('/setItem', auth, express.json(), async (req, res, next)=>{
     const svc_driver = Context.get('services').get('driver');
     let driver_result;
     try {
-        const driver_response = await svc_driver.call(
-            'puter-kvstore', 'set', {
+        const driver_response = await svc_driver.call({
+            iface: 'puter-kvstore',
+            method: 'set',
+            args: {
                 key: req.body.key,
                 value: req.body.value,
-            });
+            },
+        });
         if ( ! driver_response.success ) {
             throw new Error(driver_response.error?.message ?? 'Unknown error');
         }

+ 19 - 8
src/backend/src/services/DefaultUserService.js

@@ -201,16 +201,23 @@ class DefaultUserService extends BaseService {
         const actor = await Actor.create(UserActorType, { user });
         return await Context.get().sub({ actor }).arun(async () => {
             const svc_driver = this.services.get('driver');
-            const driver_response = await svc_driver.call(
-                'puter-kvstore', 'get', { key: 'tmp_password' });
+            const driver_response = await svc_driver.call({
+                iface: 'puter-kvstore',
+                method: 'get',
+                args: { key: 'tmp_password' },
+            });
 
             if ( driver_response.result ) return driver_response.result;
 
             const tmp_password = require('crypto').randomBytes(4).toString('hex');
-            await svc_driver.call(
-                'puter-kvstore', 'set', {
+            await svc_driver.call({
+                iface: 'puter-kvstore',
+                method: 'set',
+                args: {
                     key: 'tmp_password',
-                    value: tmp_password });
+                    value: tmp_password,
+                }
+            });
             return tmp_password;
         });
     }
@@ -223,10 +230,14 @@ class DefaultUserService extends BaseService {
             const tmp_password = require('crypto').randomBytes(4).toString('hex');
             const bcrypt = require('bcrypt');
             const password_hashed = await bcrypt.hash(tmp_password, 8);
-            await svc_driver.call(
-                'puter-kvstore', 'set', {
+            await svc_driver.call({
+                iface: 'puter-kvstore',
+                method: 'set',
+                args: {
                     key: 'tmp_password',
-                    value: tmp_password });
+                    value: tmp_password,
+                }
+            });
             await db.write(
                 `UPDATE user SET password = ? WHERE id = ?`,
                 [

+ 4 - 8
src/backend/src/services/HelloWorldService.js

@@ -2,16 +2,12 @@ const BaseService = require("./BaseService");
 
 class HelloWorldService extends BaseService {
     static IMPLEMENTS = {
-        ['driver-metadata']: {
-            get_response_meta () {
-                return {
-                    driver: 'hello-world',
-                    driver_version: 'v1.0.0',
-                    driver_interface: 'helloworld',
-                };
+        ['version']: {
+            get_version () {
+                return 'v1.0.0';
             }
         },
-        helloworld: {
+        ['hello-world']: {
             async greet ({ subject }) {
                 if ( subject ) {
                     return `Hello, ${subject}!`;

+ 4 - 0
src/backend/src/services/RegistryService.js

@@ -35,6 +35,10 @@ class MapCollection extends AdvancedBase {
     get (key) {
         return this.kv.get(this._mk_key(key));
     }
+    
+    exists (key) {
+        return this.kv.exists(this._mk_key(key));
+    }
 
     set (key, value) {
         return this.kv.set(this._mk_key(key), value);

+ 70 - 12
src/backend/src/services/drivers/DriverService.js

@@ -81,6 +81,7 @@ class DriverService extends BaseService {
             return this.interface_to_implementation[interface_name];
         }
         
+        return;
         this.log.noticeme('HERE IT IS');
         const options = this.services.get_implementors(interface_name);
         this.log.info('test', { options });
@@ -88,16 +89,17 @@ class DriverService extends BaseService {
         return options[0];
     }
 
-    async call (...a) {
+    async call (o) {
         try {
-            return await this._call(...a);
+            return await this._call(o);
         } catch ( e ) {
+            console.error(e);
             return this._driver_response_from_error(e);
         }
     }
 
-    async _call (interface_name, method, args) {
-        const processed_args = await this._process_args(interface_name, method, args);
+    async _call ({ driver, iface, method, args }) {
+        const processed_args = await this._process_args(iface, method, args);
         if ( Context.get('test_mode') ) {
             processed_args.test_mode = true;
         }
@@ -110,18 +112,44 @@ class DriverService extends BaseService {
         const services = Context.get('services');
         const svc_permission = services.get('permission');
 
-        const reading = await svc_permission.scan(actor, `driver:${interface_name}:${method}`);
+
+        const svc_registry = this.services.get('registry');
+        const c_interfaces = svc_registry.get('interfaces');
+        
+        driver = driver ?? iface;
+
+        const driver_service_exists = (() => {
+            return this.services.has(driver) &&
+                this.services.get(driver).list_traits()
+                    .includes(iface);
+        })();
+        if ( driver_service_exists ) {
+            const service = this.services.get(driver);
+            const reading = await svc_permission.scan(
+                actor,
+                PermissionUtil.join('driver', driver, 'ii', iface),
+            );
+            const options = PermissionUtil.reading_to_options(reading);
+            if ( options.length > 0 ) {
+                return await this.call_new_({
+                    service_name: driver,
+                    service,
+                    method,
+                    args: processed_args,
+                    iface,
+                });
+            }
+        }
+
+        const reading = await svc_permission.scan(actor, `driver:${iface}:${method}`);
         const options = PermissionUtil.reading_to_options(reading);
         if ( ! (options.length > 0) ) {
             throw APIError.create('permission_denied');
         }
 
-        const svc_registry = this.services.get('registry');
-        const c_interfaces = svc_registry.get('interfaces');
-
-        const instance = this.get_default_implementation(interface_name);
+        const instance = this.get_default_implementation(iface);
         if ( ! instance ) {
-            throw APIError.create('no_implementation_available', null, { interface_name })
+            throw APIError.create('no_implementation_available', null, { iface })
         }
         const meta = await (async () => {
             if ( instance instanceof Driver ) {
@@ -142,7 +170,7 @@ class DriverService extends BaseService {
                 result = await instance.impl[method](processed_args);
             }
             if ( result instanceof TypedValue ) {
-                const interface_ = c_interfaces.get(interface_name);
+                const interface_ = c_interfaces.get(iface);
                 let desired_type = interface_.methods[method]
                     .result_choices[0].type;
                 const svc_coercion = services.get('coercion');
@@ -151,8 +179,9 @@ class DriverService extends BaseService {
             }
             return { success: true, ...meta, result };
         } catch ( e ) {
+            console.error(e);
             let for_user = (e instanceof APIError) || (e instanceof DriverError);
-            if ( ! for_user ) this.errors.report(`driver:${interface_name}:${method}`, {
+            if ( ! for_user ) this.errors.report(`driver:${iface}:${method}`, {
                 source: e,
                 trace: true,
                 // TODO: alarm will not be suitable for all errors.
@@ -164,6 +193,35 @@ class DriverService extends BaseService {
             return this._driver_response_from_error(e, meta);
         }
     }
+    
+    async call_new_ ({
+        service_name,
+        service, method, args,
+        iface,
+    }) {
+        const svc_registry = this.services.get('registry');
+        const c_interfaces = svc_registry.get('interfaces');
+        let result = await service.as(iface)[method](args);
+        if ( result instanceof TypedValue ) {
+            const interface_ = c_interfaces.get(iface);
+            let desired_type = interface_.methods[method]
+                .result_choices[0].type;
+            const svc_coercion = services.get('coercion');
+            result = await svc_coercion.coerce(desired_type, result);
+        }
+        const service_meta = {};
+        if ( service.list_traits().includes('version') ) {
+            service_meta.version = service.as('version').get_version();
+        }
+        return {
+            success: true,
+            service: {
+                ...service_meta,
+                name: service_name,
+            },
+            result
+        };
+    }
 
     async _driver_response_from_error (e, meta) {
         let serializable = (e instanceof APIError) || (e instanceof DriverError);

+ 1 - 1
src/backend/src/services/drivers/interfaces.js

@@ -70,7 +70,7 @@ const ENTITY_STORAGE_INTERFACE = {
 }
 
 module.exports = {
-    'helloworld': {
+    'hello-world': {
         description: 'A simple driver that returns a greeting.',
         methods: {
             greet: {