# Script Module

The script module is automatically imported as `script`.

Represents the currently running script. The script object includes the following:

* `_id` (ObjectId) the script identifier
* `access` (Number) the acl level of access to the current in-context subject
* `arguments` (Object) the script arguments object. In triggers, there are old and new\
  properties that represent the original and modifications made to the access context subject.
* `depth` (Number) the 1-indexed script depth (a trigger firing trigger would have a depth of 2).
* `label` (String) the script label.
* `env` (Object) the current api environment
  * `host` (String) the api host (api.medable.com or api.dev.medable.com)
  * `name` (String) the environment name (production/development)
  * `url` (String) the api base endpoint (eg. <https://api.medable.com/my_org/v2>)
  * `version` (String) the current api version string
* `org` (CortexObject)
  * `_id` (ObjectId) the org identifier
  * `code` (String) the org code
* `originalPrincipal` (ObjectId) The identifier of the principal who originally called the script.\
  This may differ from the script principal if it was configured to run as a specific account.
* `principal` (CortexObject)
  * `_id` (ObjectId) the identifier of the principal who called the script.
  * `email` (String) the principal's email address
  * `name` (Object)
    * `first` (String) the principal's first name
    * `last` (String) the principal's last name
  * `roles` (ObjectId\[]) a list of the principal's assigned org roles
  * `tz` (String) the timezone for the current principal. Defaults to org tz if no timezone is available for the principal.
* `type` (String) the script type (trigger, route, job)

### Methods

[as(principal, options, function)](https://docs.medable.com/reference#section-as-principal-options-function-)\
[gc()](https://docs.medable.com/reference#section-gc-)\
[getCalloutsRemaining()](https://docs.medable.com/reference#section-getcalloutsremaining-)\
[getElapsedTime()](https://docs.medable.com/reference#section-getelapsedtime-)\
[getMemoryFree()](https://docs.medable.com/reference#section-getmemoryfree-)\
[getMemoryUsed()](https://docs.medable.com/reference#section-getmemoryused-)\
[getNotificationsRemaining()](https://docs.medable.com/reference#section-getnotificationsremaining-)\
[getOpsRemaining()](https://docs.medable.com/reference#section-getopsremaining-)\
[getOpsUsed()](https://docs.medable.com/reference#section-getopsused-)\
[getTimeLeft()](https://docs.medable.com/reference#section-gettimeleft-)

### as(principal, options, function)

Calls `function` as `principal`. `script.as()` calls can also be nested, and will always obey parent restrictions (excluding principal scope). Also available as a decorator (@as).

**Arguments**

* `principal` (String) The email or identifier of the new script principal.
* `options` (Object) An options object that limits what's accessible inside `function`.
  * `acl` (Object)
    * `safe` (Boolean=true) If true, automatically adds bypassCreateAcl, grant and skipAcl to acl.blacklist
    * `blacklist` (String\[]=null) *Adds* blacklisted options to the current set, which are filtered out of all api calls. For example, adding `skip` will cause `org.objects.accounts.find().skip(1).passthru()` to ignore the skip option.
  * `principal` (Object)
    * `roles` (ObjectID\[]=null) *Adds* roles to the current principal. To get an empty set of starting roles, call the script anonymously.
    * `scope` (String\[]=null) *Overrides* the current principal scope. To use the same parent scope, it must be passed to `script.as()`.
    * `grant` (Number=0) Sets the grant level of the calling principal. This grant level will apply to all acl operations, including list and reference read/write-through, but not to nested calls to `script.as()`. Negates the need to use the `grant()` cursor modifier.
    * `skipAcl` (Boolean=false) If true, applies skipAcl (for list query results) to all acl operations, including list and reference read/write-through, but not to nested calls to `script.as()`. Negates the need to use the `skipAcl()` cursor modifier.
    * `bypassCreateAcl` (Boolean=false) If true, applies bypassCreateAcl to all create operations, including list write-through, but not to nested calls to `script.as()`. Negates the need to use the `bypassCreateAcl()` cursor modifier.
  * `modules` (Object)
    * `safe` (Boolean=true) if true, adds a number of modules to the module blacklist (including api, cache, connections, console, debug, http, logger, notifications, objects.transfer, response, script, session).
    * `whitelist` (String\[]=null) If set, these will be the only available api interfaces. If nesting `script.as()`, the parent module.whitelist is also evaluated.
    * `blacklist` (String\[]=null) If set, these api interfaces will not be callable. If nesting `script.as()`, the parent module.blacklist is also evaluated.
* `function` (Function) The function to be called as `principal`.

**Returns**

* `value` (Object) Returns the result of the `function` argument.

### gc()

Runs garbage collection and returns the number of bytes freed.

**Returns**

* `value` (Number)

### getCalloutsRemaining()

Returns the number of [http callouts](https://docs.medable.com/cortex-api/scripting/modules-1/http-module) (http module requests) available to the current running script. Check the [Execution Limits](https://docs.medable.com/cortex-api/script-limits#execution-limits) in your org configuration for the maximum number of http callouts.

**Returns**

* `value` (Number)

### getElapsedTime()

Returns the number of milliseconds since the start of the script.

**Returns**

* `value` (Number)

### getMemoryFree()

Returns the number of bytes free for use in-script.

**Returns**

* `value` (Number)

### getMemoryUsed()

Returns the number of bytes of memory currently allocated to the script.

**Returns**

* `value` (Number)

### getNotificationsRemaining()

Returns the number of notifications.send() calls available to the current running script.

**Returns**

* `value` (Number)

### getOpsRemaining()

Returns the number of byte code operations available to the script.

**Returns**

* `value` (Number)

### getOpsUsed()

Returns the number of byte code operations consumed by the script.

**Returns**

* `value` (Number)

### getTimeLeft()

Returns the number of milliseconds remaining to the script before a timeout error is triggered.

**Returns**

* `value` (Number)

script.as() example

```
// route called anonymously
const req = require('request'),
      name = req.body.name; // "James"      

// create an object as jane@example.org
const _id = script.as(
  'jane@example.org',
  () => {
    return org.objects.c_ucumber.insertOne({c_name: name}).execute();   
  });

// update the document as john@example.org (updater will be john@example.org)
script.as(
  'john@example.org',
  {
    principal: {
      grant: consts.accessLevels.update        
    }
  },
  () => {   
    org.objects.c_ucumber.updateOne({_id: _id}, {$set: {c_name: `${name} ${name}`}}).lean(false).execute();   
  });

// read the document with: 
//   1. a role that grants `read` in the defaultAcl (a skipAcl() cursor modifier doesn't work because of the acl.safe=true default.)
//   2. a grant that allows reading the object *and* read-through owner and creator properties.
return script.as(
  script.principal._id,
  {
    principal: {
      roles: consts.roles.CucumberReader,
      grant: consts.accessLevels.read
    }
  },
  () => {   
    return org.objects.c_ucumber.find({_id: _id}).paths('c_name', 'creator.email', 'updater.email').limit(1).toArray()[0];
  });

/*
{
    "_id": "5a29cb64e00aa2e4ed8b1a68",
    "c_name": "James James",
    "creator": {
        "_id": "5a29c923e00aa2e4ed8b19eb",
        "email": "jane@example.org",
        "object": "account"
    },
    "object": "c_ucumber",
    "updater": {
        "_id": "5a29c92fe00aa2e4ed8b19f5",
        "email": "john@example.org",
        "object": "account"
    }
}
*/
```

Using @as decorator

```
import { as } from 'decorators';
import { body } from 'request';

class Protected {
  
    @as('admin@example.org', {principal: {skipAcl: true, grant: consts.accessLevels.script, roles: [consts.roles.Owner]}, modules: {safe: false}, acl: {safe: false}})
    static createOne(properties) {

        return org.objects.c_protected.insertOne(properties).execute();        

    }
}

class John {
  
    @as('john@example.com')
    static whoami() {
        return script.principal.name.first
    }
}

// script called as jane@example.org.
const _id = Protected.createOne(body); // ObjectID("5a2c43548664e438e45387cd")
const name = John.whoami(); // "John"
```
