You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/developers/applications/README.md
+39-16Lines changed: 39 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -249,9 +249,13 @@ To define custom (JavaScript) resources as endpoints, we need to create a `resou
249
249
const { Dog } = tables; // get the Dog table from the Harper provided set of tables (in the default database)
250
250
251
251
export class DogWithHumanAge extends Dog {
252
-
get(query) {
253
-
this.humanAge = 15 + this.age * 5; // silly calculation of human age equivalent
254
-
return super.get(query);
252
+
static loadAsInstance = false;
253
+
async get(target) {
254
+
const record = await super.get(target);
255
+
return {
256
+
...record, // include all properties from the record
257
+
humanAge: 15 + record.age * 5 // silly calculation of human age equivalent
258
+
};
255
259
}
256
260
}
257
261
```
@@ -269,16 +273,22 @@ type Breed @table {
269
273
}
270
274
```
271
275
272
-
Andnextwewillusethistableinour `get()` method. Wewillcallthenewtable's (static) `get()` methodtoretrieveabreedbyid. Todothiscorrectly, weaccessthetableusingourcurrentcontextbypassingin `this` asthesecondargument. Thisisimportantbecauseitensuresthatweareaccessingthedataatomically, inaconsistentsnapshotacrosstables. Thisprovidesautomaticallytrackingofmostrecentlyupdatedtimestampsacrossresourcesforcachingpurposes. Thisallowsforsharingofcontextualmetadata (like user who requested the data), andensuretransactionalatomicityforanywrites (not needed in this get operation, but important for other operations). Theresourcemethodsareautomaticallywrappedwithatransaction (will commit/finish when the method completes), andthisallowsustofullyutilizemultipleresourcesinourcurrenttransaction. WithourownsnapshotofthedatabasefortheDogandBreedtablewecanthenaccessdatalikethis:
276
+
Andnextwewillusethistableinour `get()` method. Wewillcallthenewtable's (static) `get()` methodtoretrieveabreedbyid. Harperwillmaintainthecurrentcontext, ensuringthatweareaccessingthedataatomically, inaconsistentsnapshotacrosstables. Thisprovidesautomatictrackingofmostrecentlyupdatedtimestampsacrossresourcesforcachingpurposes. Thisallowsforsharingofcontextualmetadata (like user who requested the data), andensuretransactionalatomicityforanywrites (not needed in this get operation, but important for other operations). Theresourcemethodsareautomaticallywrappedwithatransaction (will commit/finish when the method completes), andthisallowsustofullyutilizemultipleresourcesinourcurrenttransaction. WithourownsnapshotofthedatabasefortheDogandBreedtablewecanthenaccessdatalikethis:
@@ -289,9 +299,12 @@ Here we have focused on customizing how we retrieve data, but we may also want t
289
299
290
300
```javascript
291
301
exportclassCustomDogextendsDog {
292
-
asyncpost(data) {
293
-
if (data.action==='add-trick')
294
-
this.tricks.push(data.trick);
302
+
static loadAsInstance =false;
303
+
asyncpost(target, data) {
304
+
if (data.action==='add-trick') {
305
+
constrecord=this.update(target);
306
+
record.tricks.push(data.trick);
307
+
}
295
308
}
296
309
}
297
310
```
@@ -300,12 +313,22 @@ And a POST request to /CustomDog/ would call this `post` method. The Resource cl
300
313
301
314
The `post` method automatically marks the current instance as being update. However, you can also explicitly specify that you are changing a resource by calling the `update()` method. If you want to modify a resource instance that you retrieved through a `get()` call (like `Breed.get()` call above), you can call its `update()` method to ensure changes are saved (and will be committed in the current transaction).
302
315
303
-
We can also define custom authorization capabilities. For example, we might want to specify that only the owner of a dog can make updates to a dog. We could add logic to our `post` method or `put` method to do this, but we may want to separate the logic so these methods can be called separately without authorization checks. The [Resource API](../../technical-details/reference/resource.md) defines `allowRead`, `allowUpdate`, `allowCreate`, and `allowDelete`, or to easily configure individual capabilities. For example, we might do this:
316
+
We can also define custom authorization capabilities. For example, we might want to specify that only the owner of a dog can make updates to a dog. We could add logic to our `post` method or `put` method to do this. For example, we might do this:
304
317
305
318
```javascript
306
319
exportclassCustomDogextendsDog {
307
-
allowUpdate(user) {
308
-
returnthis.owner===user.username;
320
+
static loadAsInstance =false;
321
+
asyncpost(target, data) {
322
+
if (data.action==='add-trick') {
323
+
constcontext=this.getContext();
324
+
if (record.owner!==context.user?.username) {
325
+
thrownewError('Can not update this record');
326
+
}
327
+
// if we now want to skip the default permission checks, we can turn that off:
328
+
target.checkPermissions=false;
329
+
constrecord=this.update(target);
330
+
record.tricks.push(data.trick);
331
+
}
309
332
}
310
333
}
311
334
```
@@ -329,8 +352,8 @@ We can also directly implement the Resource class and use it to create new data
329
352
```javascript
330
353
const { Breed } = tables; // our Breed table
331
354
classBreedSourceextendsResource { // define a data source
Copy file name to clipboardExpand all lines: docs/technical-details/reference/resource-migration.md
+6-7Lines changed: 6 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,9 +11,8 @@ The updated Resource API is enabled on a per-class basis, by setting static `loa
11
11
* A `target` property of `checkPermission` indicates that a method should check the permission before of request before proceeding. The default instance methods provide the default authorization behavior.
12
12
* This supplants the need for `allowRead`, `allowUpdate`, `allowCreate`, and `allowDelete` methods, which shouldn't need to be used (and don't provide the id of the target record).
13
13
* Any data from a POST, PUT, and PATCH request will be available in the second argument. This reverses the order of the arguments to `put`, `post`, and `patch` compared to the legacy Resource API.
14
-
* Context is tracked using asynchronous context tracking, and will automatically be available to calls to other other resources.
15
-
* The method will return a `Updatable` object (instead of a Resource instance), which provides properties mapped to a record, but these properties can be updated and changes will be saved when the transaction is committed.
16
-
* The `update` methods will return an `Updatable` object (instead of a Resource instance), which provides properties mapped to a record, but these properties can be updated and changes will be saved when the transaction is committed.
14
+
* Context is tracked using asynchronous context tracking, and will automatically be available to calls to other resources. This can be disabled by setting `static explicitContext = true`, which can improve performance.
15
+
* The `update` method will return an `Updatable` object (instead of a Resource instance), which provides properties mapped to a record, but these properties can be updated and changes will be saved when the transaction is committed.
17
16
18
17
Here are examples of how to convert/upgrade to the non-instance binding Resource API:
19
18
Previous code with a `get` method:
@@ -45,9 +44,9 @@ export class MyData extends tables.MyData {
45
44
// we can retrieve another record from this table directly with this.get/super.get or with tables.MyData.get
46
45
record =awaitsuper.get(idWithQuery);
47
46
} else {
48
-
record =awaitsuper.get(target); // we can just directly use the query as well
47
+
record =awaitsuper.get(target); // we can just directly use the target as well
49
48
}
50
-
// the record itself is frozen, but we can copy/assign to a new record with additional properties if we want
49
+
// the record itself is frozen, but we can copy/assign to a new object with additional properties if we want
51
50
return { ...record, newProperty:'value' };
52
51
}
53
52
}
@@ -74,6 +73,7 @@ export class MyData extends tables.MyData {
74
73
// to perform/call authorization explicitly in direct get, put, post methods rather than using allow* methods.
75
74
if (!this.getContext().user) thrownewError('Unauthorized');
76
75
target.checkPermissions=false; // authorization complete, no need to further check permissions below
76
+
// target.checkPermissions is set to true or left in place, this default get method will perform the default permissions checks
77
77
returnsuper.get(target); // we can just directly use the query as well
78
78
}
79
79
}
@@ -98,14 +98,13 @@ export class MyData extends tables.MyData {
98
98
```
99
99
Updated code:
100
100
```javascript
101
-
102
101
exportclassMyDataextendstables.MyData {
103
102
static loadAsInstance =false; // opt in to updated behavior
104
103
// IMPORTANT: arguments are reversed:
105
104
asyncpost(target, data) {
106
105
let record =awaitthis.get(data.id);
107
106
if (record) { // update a property
108
-
constupdatable=awaitthis.update(data.id);
107
+
constupdatable=awaitthis.update(data.id);// we can alternately pass a target to update
0 commit comments