Skip to content

Commit d5ec400

Browse files
committed
dev: begin adding new driver call method
1 parent e0d30f0 commit d5ec400

File tree

10 files changed

+124
-37
lines changed

10 files changed

+124
-37
lines changed

src/backend/src/api/APIError.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,13 @@ module.exports = class APIError {
321321
},
322322
'no_implementation_available': {
323323
status: 502,
324-
message: ({ interface_name }) => `No implementation available for interface ${quot(interface_name)}`,
324+
message: ({
325+
iface,
326+
interface_name,
327+
driver
328+
}) => `No implementation available for ` +
329+
(iface ?? interface_name) ? 'interface' : 'driver' +
330+
' ' + quot(iface ?? interface_name ?? driver) + '.',
325331
},
326332
'method_not_found': {
327333
status: 404,

src/backend/src/data/hardcoded-permissions.js

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const implicit_user_app_permissions = [
6565
const hardcoded_user_group_permissions = {
6666
system: {
6767
'b7220104-7905-4985-b996-649fdcdb3c8f': {
68+
'service:helloworld:ii:helloworld': {},
6869
'driver:puter-kvstore': {
6970
$: 'json-address',
7071
path: '/admin/.policy/drivers.json',
@@ -87,6 +88,7 @@ const hardcoded_user_group_permissions = {
8788
},
8889
},
8990
'78b1b1dd-c959-44d2-b02c-8735671f9997': {
91+
'service:helloworld:ii:helloworld': {},
9092
'driver:puter-kvstore': {
9193
$: 'json-address',
9294
path: '/admin/.policy/drivers.json',

src/backend/src/routers/drivers/call.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ module.exports = eggspress('/drivers/call', {
5858
const interface_name = req.body.interface;
5959
const test_mode = req.body.test_mode;
6060

61-
const params = req.headers['content-type'].includes('multipart/form-data')
61+
const args = req.headers['content-type'].includes('multipart/form-data')
6262
? await _handle_multipart(req)
6363
: req.body.args;
6464

6565
let context = Context.get();
6666
if ( test_mode ) context = context.sub({ test_mode: true });
6767

6868
const result = await context.arun(async () => {
69-
return await svc_driver.call(interface_name, req.body.method, params);
69+
return await svc_driver.call({
70+
iface: interface_name,
71+
method: req.body.method,
72+
args
73+
});
7074
});
7175

7276
_respond(res, result);

src/backend/src/routers/kvstore/getItem.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ router.post('/getItem', auth, express.json(), async (req, res, next)=>{
6060
const svc_driver = Context.get('services').get('driver');
6161
let driver_result;
6262
try {
63-
const driver_response = await svc_driver.call(
64-
'puter-kvstore', 'get', { key: req.body.key });
63+
const driver_response = await svc_driver.call({
64+
iface: 'puter-kvstore',
65+
method: 'get',
66+
args: { key: req.body.key },
67+
});
6568
if ( ! driver_response.success ) {
6669
throw new Error(driver_response.error?.message ?? 'Unknown error');
6770
}

src/backend/src/routers/kvstore/setItem.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,14 @@ router.post('/setItem', auth, express.json(), async (req, res, next)=>{
7272
const svc_driver = Context.get('services').get('driver');
7373
let driver_result;
7474
try {
75-
const driver_response = await svc_driver.call(
76-
'puter-kvstore', 'set', {
75+
const driver_response = await svc_driver.call({
76+
iface: 'puter-kvstore',
77+
method: 'set',
78+
args: {
7779
key: req.body.key,
7880
value: req.body.value,
79-
});
81+
},
82+
});
8083
if ( ! driver_response.success ) {
8184
throw new Error(driver_response.error?.message ?? 'Unknown error');
8285
}

src/backend/src/services/DefaultUserService.js

+19-8
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,23 @@ class DefaultUserService extends BaseService {
201201
const actor = await Actor.create(UserActorType, { user });
202202
return await Context.get().sub({ actor }).arun(async () => {
203203
const svc_driver = this.services.get('driver');
204-
const driver_response = await svc_driver.call(
205-
'puter-kvstore', 'get', { key: 'tmp_password' });
204+
const driver_response = await svc_driver.call({
205+
iface: 'puter-kvstore',
206+
method: 'get',
207+
args: { key: 'tmp_password' },
208+
});
206209

207210
if ( driver_response.result ) return driver_response.result;
208211

209212
const tmp_password = require('crypto').randomBytes(4).toString('hex');
210-
await svc_driver.call(
211-
'puter-kvstore', 'set', {
213+
await svc_driver.call({
214+
iface: 'puter-kvstore',
215+
method: 'set',
216+
args: {
212217
key: 'tmp_password',
213-
value: tmp_password });
218+
value: tmp_password,
219+
}
220+
});
214221
return tmp_password;
215222
});
216223
}
@@ -223,10 +230,14 @@ class DefaultUserService extends BaseService {
223230
const tmp_password = require('crypto').randomBytes(4).toString('hex');
224231
const bcrypt = require('bcrypt');
225232
const password_hashed = await bcrypt.hash(tmp_password, 8);
226-
await svc_driver.call(
227-
'puter-kvstore', 'set', {
233+
await svc_driver.call({
234+
iface: 'puter-kvstore',
235+
method: 'set',
236+
args: {
228237
key: 'tmp_password',
229-
value: tmp_password });
238+
value: tmp_password,
239+
}
240+
});
230241
await db.write(
231242
`UPDATE user SET password = ? WHERE id = ?`,
232243
[

src/backend/src/services/HelloWorldService.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ const BaseService = require("./BaseService");
22

33
class HelloWorldService extends BaseService {
44
static IMPLEMENTS = {
5-
['driver-metadata']: {
6-
get_response_meta () {
7-
return {
8-
driver: 'hello-world',
9-
driver_version: 'v1.0.0',
10-
driver_interface: 'helloworld',
11-
};
5+
['version']: {
6+
get_version () {
7+
return 'v1.0.0';
128
}
139
},
14-
helloworld: {
10+
['hello-world']: {
1511
async greet ({ subject }) {
1612
if ( subject ) {
1713
return `Hello, ${subject}!`;

src/backend/src/services/RegistryService.js

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class MapCollection extends AdvancedBase {
3535
get (key) {
3636
return this.kv.get(this._mk_key(key));
3737
}
38+
39+
exists (key) {
40+
return this.kv.exists(this._mk_key(key));
41+
}
3842

3943
set (key, value) {
4044
return this.kv.set(this._mk_key(key), value);

src/backend/src/services/drivers/DriverService.js

+70-12
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,25 @@ class DriverService extends BaseService {
8181
return this.interface_to_implementation[interface_name];
8282
}
8383

84+
return;
8485
this.log.noticeme('HERE IT IS');
8586
const options = this.services.get_implementors(interface_name);
8687
this.log.info('test', { options });
8788
if ( options.length < 1 ) return;
8889
return options[0];
8990
}
9091

91-
async call (...a) {
92+
async call (o) {
9293
try {
93-
return await this._call(...a);
94+
return await this._call(o);
9495
} catch ( e ) {
96+
console.error(e);
9597
return this._driver_response_from_error(e);
9698
}
9799
}
98100

99-
async _call (interface_name, method, args) {
100-
const processed_args = await this._process_args(interface_name, method, args);
101+
async _call ({ driver, iface, method, args }) {
102+
const processed_args = await this._process_args(iface, method, args);
101103
if ( Context.get('test_mode') ) {
102104
processed_args.test_mode = true;
103105
}
@@ -110,18 +112,44 @@ class DriverService extends BaseService {
110112
const services = Context.get('services');
111113
const svc_permission = services.get('permission');
112114

113-
const reading = await svc_permission.scan(actor, `driver:${interface_name}:${method}`);
115+
116+
const svc_registry = this.services.get('registry');
117+
const c_interfaces = svc_registry.get('interfaces');
118+
119+
driver = driver ?? iface;
120+
121+
const driver_service_exists = (() => {
122+
return this.services.has(driver) &&
123+
this.services.get(driver).list_traits()
124+
.includes(iface);
125+
})();
126+
if ( driver_service_exists ) {
127+
const service = this.services.get(driver);
128+
const reading = await svc_permission.scan(
129+
actor,
130+
PermissionUtil.join('driver', driver, 'ii', iface),
131+
);
132+
const options = PermissionUtil.reading_to_options(reading);
133+
if ( options.length > 0 ) {
134+
return await this.call_new_({
135+
service_name: driver,
136+
service,
137+
method,
138+
args: processed_args,
139+
iface,
140+
});
141+
}
142+
}
143+
144+
const reading = await svc_permission.scan(actor, `driver:${iface}:${method}`);
114145
const options = PermissionUtil.reading_to_options(reading);
115146
if ( ! (options.length > 0) ) {
116147
throw APIError.create('permission_denied');
117148
}
118149

119-
const svc_registry = this.services.get('registry');
120-
const c_interfaces = svc_registry.get('interfaces');
121-
122-
const instance = this.get_default_implementation(interface_name);
150+
const instance = this.get_default_implementation(iface);
123151
if ( ! instance ) {
124-
throw APIError.create('no_implementation_available', null, { interface_name })
152+
throw APIError.create('no_implementation_available', null, { iface })
125153
}
126154
const meta = await (async () => {
127155
if ( instance instanceof Driver ) {
@@ -142,7 +170,7 @@ class DriverService extends BaseService {
142170
result = await instance.impl[method](processed_args);
143171
}
144172
if ( result instanceof TypedValue ) {
145-
const interface_ = c_interfaces.get(interface_name);
173+
const interface_ = c_interfaces.get(iface);
146174
let desired_type = interface_.methods[method]
147175
.result_choices[0].type;
148176
const svc_coercion = services.get('coercion');
@@ -151,8 +179,9 @@ class DriverService extends BaseService {
151179
}
152180
return { success: true, ...meta, result };
153181
} catch ( e ) {
182+
console.error(e);
154183
let for_user = (e instanceof APIError) || (e instanceof DriverError);
155-
if ( ! for_user ) this.errors.report(`driver:${interface_name}:${method}`, {
184+
if ( ! for_user ) this.errors.report(`driver:${iface}:${method}`, {
156185
source: e,
157186
trace: true,
158187
// TODO: alarm will not be suitable for all errors.
@@ -164,6 +193,35 @@ class DriverService extends BaseService {
164193
return this._driver_response_from_error(e, meta);
165194
}
166195
}
196+
197+
async call_new_ ({
198+
service_name,
199+
service, method, args,
200+
iface,
201+
}) {
202+
const svc_registry = this.services.get('registry');
203+
const c_interfaces = svc_registry.get('interfaces');
204+
let result = await service.as(iface)[method](args);
205+
if ( result instanceof TypedValue ) {
206+
const interface_ = c_interfaces.get(iface);
207+
let desired_type = interface_.methods[method]
208+
.result_choices[0].type;
209+
const svc_coercion = services.get('coercion');
210+
result = await svc_coercion.coerce(desired_type, result);
211+
}
212+
const service_meta = {};
213+
if ( service.list_traits().includes('version') ) {
214+
service_meta.version = service.as('version').get_version();
215+
}
216+
return {
217+
success: true,
218+
service: {
219+
...service_meta,
220+
name: service_name,
221+
},
222+
result
223+
};
224+
}
167225

168226
async _driver_response_from_error (e, meta) {
169227
let serializable = (e instanceof APIError) || (e instanceof DriverError);

src/backend/src/services/drivers/interfaces.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const ENTITY_STORAGE_INTERFACE = {
7070
}
7171

7272
module.exports = {
73-
'helloworld': {
73+
'hello-world': {
7474
description: 'A simple driver that returns a greeting.',
7575
methods: {
7676
greet: {

0 commit comments

Comments
 (0)