3
0

Merge branch 'mail-automation'

This commit is contained in:
Denis Arh
2019-09-05 19:38:12 +02:00
39 changed files with 4457 additions and 66 deletions

View File

@@ -151,9 +151,6 @@ mocks: $(GOMOCK)
########################################################################################################################
# Toolset
$(GOTEST):
$(GOGET) github.com/rakyll/gotest
$(REALIZE):
$(GOGET) github.com/tockins/realize

View File

@@ -1103,7 +1103,8 @@
}
}
]
}, {
},
{
"title": "Permissions",
"parameters": {},
"entrypoint": "permissions",
@@ -1200,5 +1201,385 @@
}
}
]
},
{
"title": "Automation scripts",
"entrypoint": "automation_script",
"path": "/automation/script",
"authentication": [
"Client ID",
"Session ID"
],
"struct": [
{
"imports": [
"github.com/cortezaproject/corteza-server/pkg/automation"
]
}
],
"apis": [
{
"name": "list",
"method": "GET",
"title": "List/read automation script",
"path": "/",
"parameters": {
"get": [
{
"name": "query",
"required": false,
"title": "Search query to match against automation script",
"type": "string"
},
{
"name": "resource",
"required": false,
"title": "Limit by resource (via trigger)",
"type": "string"
},
{
"name": "incDeleted",
"required": false,
"title": "Include deleted scripts",
"type": "bool"
},
{
"name": "page",
"type": "uint",
"required": false,
"title": "Page number (0 based)"
},
{
"name": "perPage",
"type": "uint",
"required": false,
"title": "Returned items per page (default 50)"
}
]
}
},
{
"name": "create",
"method": "POST",
"title": "Add new automation script",
"path": "/",
"parameters": {
"post": [
{
"name": "name",
"title": "automation name",
"type": "string"
},
{
"name": "sourceRef",
"title": "Source URL",
"type": "string"
},
{
"name": "source",
"title": "Source code",
"type": "string"
},
{
"name": "runAs",
"title": "Run as specific user",
"type": "uint64"
},
{
"name": "timeout",
"title": "Script timeout (in milliseconds)",
"type": "uint"
},
{
"name": "critical",
"title": "Is it critical to run this script successfully",
"type": "bool"
},
{
"name": "async",
"title": "Will this script be ran asynchronously",
"type": "bool"
},
{
"name": "enabled",
"type": "bool"
},
{
"name": "triggers",
"type": "automation.TriggerSet"
}
]
}
},
{
"name": "read",
"method": "GET",
"title": "Read automation script by ID",
"path": "/{scriptID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "scriptID",
"required": true,
"title": "automation script ID"
}
]
}
},
{
"name": "update",
"method": "POST",
"title": "Update automation script",
"path": "/{scriptID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "scriptID",
"required": true,
"title": "Automation script ID"
}
],
"post": [
{
"name": "name",
"title": "Script name",
"type": "string"
},
{
"name": "sourceRef",
"title": "Source URL",
"type": "string"
},
{
"name": "source",
"title": "Source code",
"type": "string"
},
{
"name": "runAs",
"title": "Run script as specific user",
"type": "uint64"
},
{
"name": "timeout",
"title": "Run script in user-agent (browser)",
"type": "uint"
},
{
"name": "critical",
"title": "Is it critical to run this script successfully",
"type": "bool"
},
{
"name": "async",
"title": "Will this script be ran asynchronously",
"type": "bool"
},
{
"name": "enabled",
"type": "bool"
},
{
"name": "triggers",
"type": "automation.TriggerSet"
}
]
}
},
{
"name": "delete",
"method": "DELETE",
"title": "Delete script",
"path": "/{scriptID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "scriptID",
"required": true,
"title": "Script ID"
}
]
}
},
{
"name": "test",
"method": "POST",
"title": "Run source code in corredor. Used for testing",
"path": "/test",
"parameters": {
"post": [
{"name": "source", "type": "string", "title": "Script's source code"},
{"name": "payload", "type": "json.RawMessage", "title": "Payload to be used"}
]
}
}
]
},
{
"title": "Automation script triggers",
"entrypoint": "automation_trigger",
"path": "/automation/script/{scriptID}/trigger",
"authentication": [
"Client ID",
"Session ID"
],
"parameters": {
"path": [
{
"type": "uint64",
"name": "scriptID",
"required": true,
"title": "Script ID"
}
]
},
"apis": [
{
"name": "list",
"method": "GET",
"title": "List/read automation script triggers",
"path": "/",
"parameters": {
"get": [
{
"name": "resource",
"required": false,
"title": "Only triggers of a specific resource",
"type": "string"
},
{
"name": "event",
"required": false,
"title": "Only triggers of a specific event",
"type": "string"
},
{
"name": "incDeleted",
"required": false,
"title": "Include deleted scripts",
"type": "bool"
},
{
"name": "page",
"type": "uint",
"required": false,
"title": "Page number (0 based)"
},
{
"name": "perPage",
"type": "uint",
"required": false,
"title": "Returned items per page (default 50)"
}
]
}
},
{
"name": "create",
"method": "POST",
"title": "Add new automation script trigger",
"path": "/",
"parameters": {
"post": [
{
"name": "resource",
"title": "Resource",
"type": "string",
"required": true
},
{
"name": "event",
"title": "Event",
"type": "string",
"required": true
},
{
"name": "condition",
"title": "Event",
"type": "string"
},
{
"name": "enabled",
"type": "bool"
}
]
}
},
{
"name": "read",
"method": "GET",
"title": "Read automation script trigger by ID",
"path": "/{triggerID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID"
}
]
}
},
{
"name": "update",
"method": "POST",
"title": "Update automation script trigger",
"path": "/{triggerID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID"
}
],
"post": [
{
"name": "resource",
"title": "Resource",
"type": "string",
"required": true
},
{
"name": "event",
"title": "Event",
"type": "string",
"required": true
},
{
"name": "condition",
"title": "Event",
"type": "string"
},
{
"name": "enabled",
"type": "bool"
}
]
}
},
{
"name": "delete",
"method": "DELETE",
"title": "Delete script",
"path": "/{triggerID}",
"parameters": {
"path": [
{
"type": "uint64",
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID"
}
]
}
}
]
}
]

View File

@@ -0,0 +1,226 @@
{
"Title": "Automation scripts",
"Interface": "Automation_script",
"Struct": [
{
"imports": [
"github.com/cortezaproject/corteza-server/pkg/automation"
]
}
],
"Parameters": null,
"Protocol": "",
"Authentication": [
"Client ID",
"Session ID"
],
"Path": "/automation/script",
"APIs": [
{
"Name": "list",
"Method": "GET",
"Title": "List/read automation script",
"Path": "/",
"Parameters": {
"get": [
{
"name": "query",
"required": false,
"title": "Search query to match against automation script",
"type": "string"
},
{
"name": "resource",
"required": false,
"title": "Limit by resource (via trigger)",
"type": "string"
},
{
"name": "incDeleted",
"required": false,
"title": "Include deleted scripts",
"type": "bool"
},
{
"name": "page",
"required": false,
"title": "Page number (0 based)",
"type": "uint"
},
{
"name": "perPage",
"required": false,
"title": "Returned items per page (default 50)",
"type": "uint"
}
]
}
},
{
"Name": "create",
"Method": "POST",
"Title": "Add new automation script",
"Path": "/",
"Parameters": {
"post": [
{
"name": "name",
"title": "automation name",
"type": "string"
},
{
"name": "sourceRef",
"title": "Source URL",
"type": "string"
},
{
"name": "source",
"title": "Source code",
"type": "string"
},
{
"name": "runAs",
"title": "Run as specific user",
"type": "uint64"
},
{
"name": "timeout",
"title": "Script timeout (in milliseconds)",
"type": "uint"
},
{
"name": "critical",
"title": "Is it critical to run this script successfully",
"type": "bool"
},
{
"name": "async",
"title": "Will this script be ran asynchronously",
"type": "bool"
},
{
"name": "enabled",
"type": "bool"
},
{
"name": "triggers",
"type": "automation.TriggerSet"
}
]
}
},
{
"Name": "read",
"Method": "GET",
"Title": "Read automation script by ID",
"Path": "/{scriptID}",
"Parameters": {
"path": [
{
"name": "scriptID",
"required": true,
"title": "automation script ID",
"type": "uint64"
}
]
}
},
{
"Name": "update",
"Method": "POST",
"Title": "Update automation script",
"Path": "/{scriptID}",
"Parameters": {
"path": [
{
"name": "scriptID",
"required": true,
"title": "Automation script ID",
"type": "uint64"
}
],
"post": [
{
"name": "name",
"title": "Script name",
"type": "string"
},
{
"name": "sourceRef",
"title": "Source URL",
"type": "string"
},
{
"name": "source",
"title": "Source code",
"type": "string"
},
{
"name": "runAs",
"title": "Run script as specific user",
"type": "uint64"
},
{
"name": "timeout",
"title": "Run script in user-agent (browser)",
"type": "uint"
},
{
"name": "critical",
"title": "Is it critical to run this script successfully",
"type": "bool"
},
{
"name": "async",
"title": "Will this script be ran asynchronously",
"type": "bool"
},
{
"name": "enabled",
"type": "bool"
},
{
"name": "triggers",
"type": "automation.TriggerSet"
}
]
}
},
{
"Name": "delete",
"Method": "DELETE",
"Title": "Delete script",
"Path": "/{scriptID}",
"Parameters": {
"path": [
{
"name": "scriptID",
"required": true,
"title": "Script ID",
"type": "uint64"
}
]
}
},
{
"Name": "test",
"Method": "POST",
"Title": "Run source code in corredor. Used for testing",
"Path": "/test",
"Parameters": {
"post": [
{
"name": "source",
"title": "Script's source code",
"type": "string"
},
{
"name": "payload",
"title": "Payload to be used",
"type": "json.RawMessage"
}
]
}
}
]
}

View File

@@ -0,0 +1,165 @@
{
"Title": "Automation script triggers",
"Interface": "Automation_trigger",
"Struct": null,
"Parameters": {
"path": [
{
"name": "scriptID",
"required": true,
"title": "Script ID",
"type": "uint64"
}
]
},
"Protocol": "",
"Authentication": [
"Client ID",
"Session ID"
],
"Path": "/automation/script/{scriptID}/trigger",
"APIs": [
{
"Name": "list",
"Method": "GET",
"Title": "List/read automation script triggers",
"Path": "/",
"Parameters": {
"get": [
{
"name": "resource",
"required": false,
"title": "Only triggers of a specific resource",
"type": "string"
},
{
"name": "event",
"required": false,
"title": "Only triggers of a specific event",
"type": "string"
},
{
"name": "incDeleted",
"required": false,
"title": "Include deleted scripts",
"type": "bool"
},
{
"name": "page",
"required": false,
"title": "Page number (0 based)",
"type": "uint"
},
{
"name": "perPage",
"required": false,
"title": "Returned items per page (default 50)",
"type": "uint"
}
]
}
},
{
"Name": "create",
"Method": "POST",
"Title": "Add new automation script trigger",
"Path": "/",
"Parameters": {
"post": [
{
"name": "resource",
"required": true,
"title": "Resource",
"type": "string"
},
{
"name": "event",
"required": true,
"title": "Event",
"type": "string"
},
{
"name": "condition",
"title": "Event",
"type": "string"
},
{
"name": "enabled",
"type": "bool"
}
]
}
},
{
"Name": "read",
"Method": "GET",
"Title": "Read automation script trigger by ID",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID",
"type": "uint64"
}
]
}
},
{
"Name": "update",
"Method": "POST",
"Title": "Update automation script trigger",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID",
"type": "uint64"
}
],
"post": [
{
"name": "resource",
"required": true,
"title": "Resource",
"type": "string"
},
{
"name": "event",
"required": true,
"title": "Event",
"type": "string"
},
{
"name": "condition",
"title": "Event",
"type": "string"
},
{
"name": "enabled",
"type": "bool"
}
]
}
},
{
"Name": "delete",
"Method": "DELETE",
"Title": "Delete script",
"Path": "/{triggerID}",
"Parameters": {
"path": [
{
"name": "triggerID",
"required": true,
"title": "Automation script trigger ID",
"type": "uint64"
}
]
}
}
]
}

View File

@@ -164,8 +164,8 @@ function proto {
CORTEZA_PROTOBUF_PATH=${CORTEZA_PROTOBUF_PATH:-"vendor/github.com/cortezaproject/corteza-protobuf"}
# Compose Proto Path
CPC="github.com/cortezaproject/corteza-server/compose/proto"
ComposeProtoPath="github.com/cortezaproject/corteza-server/compose/proto"
SystemProtoPath="github.com/cortezaproject/corteza-server/system/proto"
yellow " ${CORTEZA_PROTOBUF_PATH} >> compose/proto"
PATH=$PATH:$GOPATH/bin protoc \
@@ -174,9 +174,10 @@ function proto {
record.proto namespace.proto module.proto
assoc="${assoc},Mcompose/record.proto=${CPC}"
assoc="${assoc},Mcompose/module.proto=${CPC}"
assoc="${assoc},Mcompose/namespace.proto=${CPC}"
assoc="${assoc},Mcompose/record.proto=${ComposeProtoPath}"
assoc="${assoc},Mcompose/module.proto=${ComposeProtoPath}"
assoc="${assoc},Mcompose/namespace.proto=${ComposeProtoPath}"
assoc="${assoc},Msystem/mail_message.proto=${SystemProtoPath}"
yellow " ${CORTEZA_PROTOBUF_PATH} >> pkg/automation/corredor"
PATH=$PATH:$GOPATH/bin protoc \
@@ -188,7 +189,7 @@ function proto {
PATH=$PATH:$GOPATH/bin protoc \
--proto_path ${CORTEZA_PROTOBUF_PATH}/system \
--go_out=plugins=grpc:system/proto \
user.proto
user.proto mail_message.proto
green "OK"
}

File diff suppressed because one or more lines are too long

View File

@@ -26,7 +26,7 @@ CREATE TABLE IF NOT EXISTS compose_automation_script (
CREATE TABLE IF NOT EXISTS compose_automation_trigger (
`id` BIGINT(20) UNSIGNED NOT NULL,
`rel_script` BIGINT(20) UNSIGNED NOT NULL COMMENT 'Script that is trigger',
`rel_script` BIGINT(20) UNSIGNED NOT NULL COMMENT 'Script that is triggered',
`resource` VARCHAR(128) NOT NULL COMMENT 'Resource triggering the event',
`event` VARCHAR(128) NOT NULL COMMENT 'Event triggered',

View File

@@ -32,7 +32,7 @@ type (
var (
DefaultLogger *zap.Logger
// DefaultPermissions Retrives & stores permissions
// DefaultPermissions Retrieves & stores permissions
DefaultPermissions permissionServicer
// DefaultAccessControl Access control checking

View File

@@ -63,10 +63,6 @@ func (AutomationTrigger) New() *AutomationTrigger {
func (ctrl AutomationTrigger) List(ctx context.Context, r *request.AutomationTriggerList) (interface{}, error) {
set, filter, err := ctrl.triggers.Find(ctx, automation.TriggerFilter{
// @todo namespace filtering
// Might be a bit tricky as triggers themselves not know about namespaces
// Namespace: r.NamespaceID
Resource: r.Resource,
Event: r.Event,
ScriptID: r.ScriptID,

View File

@@ -17,8 +17,8 @@ func MountRoutes(r chi.Router) {
notification = Notification{}.New()
attachment = Attachment{}.New()
automationScript = AutomationScript{}.New()
automationTrgger = AutomationTrigger{}.New()
automationScript = AutomationScript{}.New()
automationTrigger = AutomationTrigger{}.New()
)
// Initialize handlers & controllers.
@@ -41,6 +41,6 @@ func MountRoutes(r chi.Router) {
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
handlers.NewAutomationScript(automationScript).MountRoutes(r)
handlers.NewAutomationTrigger(automationTrgger).MountRoutes(r)
handlers.NewAutomationTrigger(automationTrigger).MountRoutes(r)
})
}

View File

@@ -278,6 +278,229 @@
# Automation scripts
| Method | Endpoint | Purpose |
| ------ | -------- | ------- |
| `GET` | `/automation/script/` | List/read automation script |
| `POST` | `/automation/script/` | Add new automation script |
| `GET` | `/automation/script/{scriptID}` | Read automation script by ID |
| `POST` | `/automation/script/{scriptID}` | Update automation script |
| `DELETE` | `/automation/script/{scriptID}` | Delete script |
| `POST` | `/automation/script/test` | Run source code in corredor. Used for testing |
## List/read automation script
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/` | HTTP/S | GET | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| query | string | GET | Search query to match against automation script | N/A | NO |
| resource | string | GET | Limit by resource (via trigger) | N/A | NO |
| incDeleted | bool | GET | Include deleted scripts | N/A | NO |
| page | uint | GET | Page number (0 based) | N/A | NO |
| perPage | uint | GET | Returned items per page (default 50) | N/A | NO |
## Add new automation script
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/` | HTTP/S | POST | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| name | string | POST | automation name | N/A | NO |
| sourceRef | string | POST | Source URL | N/A | NO |
| source | string | POST | Source code | N/A | NO |
| runAs | uint64 | POST | Run as specific user | N/A | NO |
| timeout | uint | POST | Script timeout (in milliseconds) | N/A | NO |
| critical | bool | POST | Is it critical to run this script successfully | N/A | NO |
| async | bool | POST | Will this script be ran asynchronously | N/A | NO |
| enabled | bool | POST | | N/A | NO |
| triggers | automation.TriggerSet | POST | | N/A | NO |
## Read automation script by ID
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}` | HTTP/S | GET | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| scriptID | uint64 | PATH | automation script ID | N/A | YES |
## Update automation script
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}` | HTTP/S | POST | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| scriptID | uint64 | PATH | Automation script ID | N/A | YES |
| name | string | POST | Script name | N/A | NO |
| sourceRef | string | POST | Source URL | N/A | NO |
| source | string | POST | Source code | N/A | NO |
| runAs | uint64 | POST | Run script as specific user | N/A | NO |
| timeout | uint | POST | Run script in user-agent (browser) | N/A | NO |
| critical | bool | POST | Is it critical to run this script successfully | N/A | NO |
| async | bool | POST | Will this script be ran asynchronously | N/A | NO |
| enabled | bool | POST | | N/A | NO |
| triggers | automation.TriggerSet | POST | | N/A | NO |
## Delete script
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}` | HTTP/S | DELETE | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
## Run source code in corredor. Used for testing
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/test` | HTTP/S | POST | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| source | string | POST | Script's source code | N/A | NO |
| payload | json.RawMessage | POST | Payload to be used | N/A | NO |
---
# Automation script triggers
| Method | Endpoint | Purpose |
| ------ | -------- | ------- |
| `GET` | `/automation/script/{scriptID}/trigger/` | List/read automation script triggers |
| `POST` | `/automation/script/{scriptID}/trigger/` | Add new automation script trigger |
| `GET` | `/automation/script/{scriptID}/trigger/{triggerID}` | Read automation script trigger by ID |
| `POST` | `/automation/script/{scriptID}/trigger/{triggerID}` | Update automation script trigger |
| `DELETE` | `/automation/script/{scriptID}/trigger/{triggerID}` | Delete script |
## List/read automation script triggers
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}/trigger/` | HTTP/S | GET | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| resource | string | GET | Only triggers of a specific resource | N/A | NO |
| event | string | GET | Only triggers of a specific event | N/A | NO |
| incDeleted | bool | GET | Include deleted scripts | N/A | NO |
| page | uint | GET | Page number (0 based) | N/A | NO |
| perPage | uint | GET | Returned items per page (default 50) | N/A | NO |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
## Add new automation script trigger
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}/trigger/` | HTTP/S | POST | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| resource | string | POST | Resource | N/A | YES |
| event | string | POST | Event | N/A | YES |
| condition | string | POST | Event | N/A | NO |
| enabled | bool | POST | | N/A | NO |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
## Read automation script trigger by ID
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}/trigger/{triggerID}` | HTTP/S | GET | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| triggerID | uint64 | PATH | Automation script trigger ID | N/A | YES |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
## Update automation script trigger
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}/trigger/{triggerID}` | HTTP/S | POST | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| triggerID | uint64 | PATH | Automation script trigger ID | N/A | YES |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
| resource | string | POST | Resource | N/A | YES |
| event | string | POST | Event | N/A | YES |
| condition | string | POST | Event | N/A | NO |
| enabled | bool | POST | | N/A | NO |
## Delete script
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/automation/script/{scriptID}/trigger/{triggerID}` | HTTP/S | DELETE | Client ID, Session ID |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| triggerID | uint64 | PATH | Automation script trigger ID | N/A | YES |
| scriptID | uint64 | PATH | Script ID | N/A | YES |
---
# Organisations
Organisations represent a top-level grouping entity. There may be many organisations defined in a single deployment.

View File

@@ -7,6 +7,7 @@ import (
context "context"
fmt "fmt"
proto1 "github.com/cortezaproject/corteza-server/compose/proto"
proto2 "github.com/cortezaproject/corteza-server/system/proto"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
@@ -261,6 +262,61 @@ func (m *RunRecordRequest) GetRecord() *proto1.Record {
return nil
}
type RunMailMessageRequest struct {
Config map[string]string `protobuf:"bytes,1,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Script *Script `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"`
MailMessage *proto2.MailMessage `protobuf:"bytes,3,opt,name=mailMessage,proto3" json:"mailMessage,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RunMailMessageRequest) Reset() { *m = RunMailMessageRequest{} }
func (m *RunMailMessageRequest) String() string { return proto.CompactTextString(m) }
func (*RunMailMessageRequest) ProtoMessage() {}
func (*RunMailMessageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{4}
}
func (m *RunMailMessageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RunMailMessageRequest.Unmarshal(m, b)
}
func (m *RunMailMessageRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RunMailMessageRequest.Marshal(b, m, deterministic)
}
func (m *RunMailMessageRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RunMailMessageRequest.Merge(m, src)
}
func (m *RunMailMessageRequest) XXX_Size() int {
return xxx_messageInfo_RunMailMessageRequest.Size(m)
}
func (m *RunMailMessageRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RunMailMessageRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RunMailMessageRequest proto.InternalMessageInfo
func (m *RunMailMessageRequest) GetConfig() map[string]string {
if m != nil {
return m.Config
}
return nil
}
func (m *RunMailMessageRequest) GetScript() *Script {
if m != nil {
return m.Script
}
return nil
}
func (m *RunMailMessageRequest) GetMailMessage() *proto2.MailMessage {
if m != nil {
return m.MailMessage
}
return nil
}
type RunTestResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@@ -271,7 +327,7 @@ func (m *RunTestResponse) Reset() { *m = RunTestResponse{} }
func (m *RunTestResponse) String() string { return proto.CompactTextString(m) }
func (*RunTestResponse) ProtoMessage() {}
func (*RunTestResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{4}
return fileDescriptor_c06bb92bf45e37e2, []int{5}
}
func (m *RunTestResponse) XXX_Unmarshal(b []byte) error {
@@ -303,7 +359,7 @@ func (m *RunNamespaceResponse) Reset() { *m = RunNamespaceResponse{} }
func (m *RunNamespaceResponse) String() string { return proto.CompactTextString(m) }
func (*RunNamespaceResponse) ProtoMessage() {}
func (*RunNamespaceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{5}
return fileDescriptor_c06bb92bf45e37e2, []int{6}
}
func (m *RunNamespaceResponse) XXX_Unmarshal(b []byte) error {
@@ -342,7 +398,7 @@ func (m *RunModuleResponse) Reset() { *m = RunModuleResponse{} }
func (m *RunModuleResponse) String() string { return proto.CompactTextString(m) }
func (*RunModuleResponse) ProtoMessage() {}
func (*RunModuleResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{6}
return fileDescriptor_c06bb92bf45e37e2, []int{7}
}
func (m *RunModuleResponse) XXX_Unmarshal(b []byte) error {
@@ -381,7 +437,7 @@ func (m *RunRecordResponse) Reset() { *m = RunRecordResponse{} }
func (m *RunRecordResponse) String() string { return proto.CompactTextString(m) }
func (*RunRecordResponse) ProtoMessage() {}
func (*RunRecordResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{7}
return fileDescriptor_c06bb92bf45e37e2, []int{8}
}
func (m *RunRecordResponse) XXX_Unmarshal(b []byte) error {
@@ -409,6 +465,37 @@ func (m *RunRecordResponse) GetRecord() *proto1.Record {
return nil
}
type RunMailMessageResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RunMailMessageResponse) Reset() { *m = RunMailMessageResponse{} }
func (m *RunMailMessageResponse) String() string { return proto.CompactTextString(m) }
func (*RunMailMessageResponse) ProtoMessage() {}
func (*RunMailMessageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{9}
}
func (m *RunMailMessageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RunMailMessageResponse.Unmarshal(m, b)
}
func (m *RunMailMessageResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RunMailMessageResponse.Marshal(b, m, deterministic)
}
func (m *RunMailMessageResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RunMailMessageResponse.Merge(m, src)
}
func (m *RunMailMessageResponse) XXX_Size() int {
return xxx_messageInfo_RunMailMessageResponse.Size(m)
}
func (m *RunMailMessageResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RunMailMessageResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RunMailMessageResponse proto.InternalMessageInfo
type Script struct {
Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
@@ -423,7 +510,7 @@ func (m *Script) Reset() { *m = Script{} }
func (m *Script) String() string { return proto.CompactTextString(m) }
func (*Script) ProtoMessage() {}
func (*Script) Descriptor() ([]byte, []int) {
return fileDescriptor_c06bb92bf45e37e2, []int{8}
return fileDescriptor_c06bb92bf45e37e2, []int{10}
}
func (m *Script) XXX_Unmarshal(b []byte) error {
@@ -480,48 +567,56 @@ func init() {
proto.RegisterMapType((map[string]string)(nil), "corredor.RunModuleRequest.ConfigEntry")
proto.RegisterType((*RunRecordRequest)(nil), "corredor.RunRecordRequest")
proto.RegisterMapType((map[string]string)(nil), "corredor.RunRecordRequest.ConfigEntry")
proto.RegisterType((*RunMailMessageRequest)(nil), "corredor.RunMailMessageRequest")
proto.RegisterMapType((map[string]string)(nil), "corredor.RunMailMessageRequest.ConfigEntry")
proto.RegisterType((*RunTestResponse)(nil), "corredor.RunTestResponse")
proto.RegisterType((*RunNamespaceResponse)(nil), "corredor.RunNamespaceResponse")
proto.RegisterType((*RunModuleResponse)(nil), "corredor.RunModuleResponse")
proto.RegisterType((*RunRecordResponse)(nil), "corredor.RunRecordResponse")
proto.RegisterType((*RunMailMessageResponse)(nil), "corredor.RunMailMessageResponse")
proto.RegisterType((*Script)(nil), "corredor.Script")
}
func init() { proto.RegisterFile("service-corredor.proto", fileDescriptor_c06bb92bf45e37e2) }
var fileDescriptor_c06bb92bf45e37e2 = []byte{
// 492 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xdd, 0x8a, 0x13, 0x31,
0x14, 0x26, 0x6d, 0x77, 0xdc, 0x9e, 0xaa, 0xdb, 0x8d, 0x65, 0x8d, 0x23, 0xca, 0x32, 0x17, 0x5a,
0x2f, 0x1c, 0xa5, 0xde, 0xf8, 0xb3, 0x08, 0x8b, 0x08, 0x22, 0xe8, 0x45, 0xf4, 0x05, 0x6a, 0x1a,
0xa5, 0xb8, 0x93, 0x8c, 0xc9, 0x64, 0xa1, 0xaf, 0xe1, 0xbd, 0x4f, 0xe2, 0x43, 0xf9, 0x0a, 0x32,
0xf9, 0x99, 0x69, 0xc6, 0x6e, 0x59, 0x29, 0x88, 0x77, 0x73, 0xce, 0xf9, 0x72, 0xce, 0x7c, 0xdf,
0xf9, 0x81, 0x23, 0xcd, 0xd5, 0xf9, 0x92, 0xf1, 0x87, 0x4c, 0x2a, 0xc5, 0x17, 0x52, 0xe5, 0xa5,
0x92, 0x95, 0xc4, 0xfb, 0xc1, 0x4e, 0x6f, 0x32, 0x59, 0x94, 0x52, 0xf3, 0x47, 0x62, 0x5e, 0x70,
0x5d, 0xce, 0x19, 0x77, 0x90, 0x74, 0x12, 0x02, 0x85, 0x5c, 0x98, 0xb3, 0x3f, 0xbc, 0x8a, 0x33,
0xa9, 0x16, 0xce, 0x9b, 0x9d, 0xc0, 0x75, 0x6a, 0xc4, 0x47, 0xae, 0x2b, 0xca, 0xbf, 0x19, 0xae,
0x2b, 0x7c, 0x04, 0x89, 0x96, 0x46, 0x31, 0x4e, 0xd0, 0x31, 0x9a, 0x0e, 0xa9, 0xb7, 0x30, 0x86,
0x41, 0x5d, 0x88, 0xf4, 0xac, 0xd7, 0x7e, 0x67, 0xbf, 0x10, 0xdc, 0xa0, 0x46, 0xbc, 0x0f, 0x3f,
0x10, 0x72, 0x9c, 0x42, 0xc2, 0xa4, 0xf8, 0xbc, 0xfc, 0x42, 0xd0, 0x71, 0x7f, 0x3a, 0x9a, 0x3d,
0xc8, 0x1b, 0x16, 0x1b, 0xe0, 0xf9, 0x2b, 0x8b, 0x7d, 0x2d, 0x2a, 0xb5, 0xa2, 0xfe, 0x21, 0x9e,
0x42, 0xa2, 0x99, 0x5a, 0x96, 0x95, 0x2d, 0x38, 0x9a, 0x8d, 0xdb, 0x14, 0x1f, 0xac, 0x9f, 0xfa,
0x38, 0x7e, 0x0c, 0xc3, 0x46, 0x01, 0xd2, 0xb7, 0x60, 0x9c, 0x7b, 0xb2, 0x79, 0x5b, 0xab, 0x05,
0xa5, 0xcf, 0x60, 0xb4, 0x56, 0x12, 0x8f, 0xa1, 0xff, 0x95, 0xaf, 0x3c, 0xdd, 0xfa, 0x13, 0x4f,
0x60, 0xef, 0x7c, 0x7e, 0x66, 0x02, 0x59, 0x67, 0x3c, 0xef, 0x3d, 0x45, 0xd9, 0xf7, 0x1e, 0x8c,
0xa9, 0x11, 0xef, 0xac, 0xb2, 0x81, 0xee, 0xcb, 0x0e, 0xdd, 0x7b, 0x11, 0xdd, 0x08, 0xfb, 0xaf,
0xb9, 0xe2, 0xfb, 0x90, 0xb8, 0x31, 0x20, 0x03, 0x0b, 0x3f, 0x68, 0xe0, 0xfe, 0xbf, 0x7c, 0x78,
0x17, 0x51, 0x7e, 0x3a, 0x51, 0xa8, 0x1d, 0xac, 0xcb, 0x89, 0x12, 0x61, 0xff, 0x5b, 0x51, 0x6a,
0xa0, 0x5b, 0x17, 0xb2, 0xd7, 0x01, 0x7a, 0x02, 0x3e, 0xbc, 0x8b, 0x7a, 0x87, 0x70, 0xd0, 0xac,
0xa0, 0x2e, 0xa5, 0xd0, 0x3c, 0x7b, 0x03, 0x93, 0x78, 0x4f, 0x9c, 0x3f, 0x66, 0x8a, 0x2e, 0xc1,
0x34, 0x3b, 0x81, 0xc3, 0xb5, 0x11, 0xf4, 0x69, 0x5a, 0xfa, 0x68, 0x2b, 0x7d, 0xff, 0x3a, 0xf4,
0xaa, 0x7d, 0xed, 0x35, 0x41, 0x5b, 0x35, 0xc9, 0x16, 0x90, 0xb8, 0x4e, 0xfd, 0xcd, 0x4d, 0xc1,
0x04, 0xae, 0x54, 0xcb, 0x82, 0x4b, 0x53, 0xd9, 0x5e, 0x5e, 0xa3, 0xc1, 0xac, 0x25, 0x9c, 0xeb,
0x95, 0x60, 0xb6, 0x69, 0xfb, 0xd4, 0x19, 0xb3, 0x1f, 0x3d, 0xb8, 0xea, 0x07, 0xc2, 0x08, 0xc1,
0x15, 0x7e, 0x01, 0x83, 0x5a, 0x4c, 0x4c, 0xa2, 0x81, 0x5b, 0x3b, 0x71, 0xe9, 0xad, 0x0d, 0x11,
0x4f, 0xee, 0x2d, 0x0c, 0x1b, 0x1d, 0xf1, 0x9d, 0xad, 0x67, 0x2b, 0xbd, 0x7b, 0x51, 0xd8, 0xe7,
0x3a, 0x85, 0xc4, 0xe9, 0x89, 0xd3, 0x8b, 0x0f, 0x42, 0x7a, 0x7b, 0x63, 0xac, 0x4d, 0xe1, 0x44,
0xed, 0xa4, 0x88, 0xd6, 0xa7, 0x93, 0x22, 0x6e, 0xd7, 0xa7, 0xc4, 0x1e, 0xfa, 0x27, 0xbf, 0x03,
0x00, 0x00, 0xff, 0xff, 0x72, 0x3d, 0x15, 0x27, 0x51, 0x06, 0x00, 0x00,
// 576 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x55, 0x5f, 0x6b, 0x13, 0x41,
0x10, 0xe7, 0x92, 0xf4, 0x6c, 0x26, 0x6a, 0xd3, 0x6d, 0x8c, 0xd7, 0x13, 0x35, 0xdc, 0x83, 0x46,
0xc4, 0xab, 0x44, 0x04, 0xff, 0x14, 0xa1, 0x14, 0x41, 0x84, 0x8a, 0xac, 0xbe, 0xcb, 0x79, 0x59,
0xcb, 0x61, 0x6e, 0x37, 0xee, 0xde, 0x15, 0xf2, 0x35, 0xfc, 0x3a, 0x7e, 0xa8, 0xbe, 0xfa, 0x28,
0xb7, 0x3b, 0xf7, 0xb7, 0x49, 0xac, 0x04, 0xa4, 0x6f, 0x37, 0x33, 0xbf, 0x99, 0xdd, 0xdf, 0x6f,
0x66, 0xe7, 0x60, 0xa8, 0x98, 0x3c, 0x8b, 0x42, 0xf6, 0x24, 0x14, 0x52, 0xb2, 0xa9, 0x90, 0xfe,
0x5c, 0x8a, 0x44, 0x90, 0xed, 0xdc, 0x76, 0x6f, 0x87, 0x22, 0x9e, 0x0b, 0xc5, 0x0e, 0x78, 0x10,
0x33, 0x35, 0x0f, 0x42, 0x66, 0x20, 0xee, 0x20, 0x0f, 0xc4, 0x62, 0x9a, 0xce, 0x2e, 0x78, 0x25,
0x0b, 0x85, 0x9c, 0xa2, 0x77, 0x5f, 0x2d, 0x54, 0xc2, 0xe2, 0x83, 0x38, 0x88, 0x66, 0x5f, 0x62,
0xa6, 0x54, 0x70, 0x8a, 0x09, 0xde, 0x21, 0xdc, 0xa4, 0x29, 0xff, 0xcc, 0x54, 0x42, 0xd9, 0x8f,
0x94, 0xa9, 0x84, 0x0c, 0xc1, 0x56, 0x22, 0x95, 0x21, 0x73, 0xac, 0x91, 0x35, 0xee, 0x52, 0xb4,
0x08, 0x81, 0x4e, 0x76, 0x07, 0xa7, 0xa5, 0xbd, 0xfa, 0xdb, 0x3b, 0xb7, 0x60, 0x8f, 0xa6, 0xfc,
0x43, 0x7e, 0xb7, 0xbc, 0xc6, 0x11, 0xd8, 0xa1, 0xe0, 0xdf, 0xa2, 0x53, 0xc7, 0x1a, 0xb5, 0xc7,
0xbd, 0xc9, 0x23, 0xbf, 0x20, 0xb8, 0x04, 0xee, 0x1f, 0x6b, 0xec, 0x5b, 0x9e, 0xc8, 0x05, 0xc5,
0x44, 0x32, 0x06, 0x5b, 0x85, 0x32, 0x9a, 0x27, 0xfa, 0xc0, 0xde, 0xa4, 0x5f, 0x96, 0xf8, 0xa4,
0xfd, 0x14, 0xe3, 0xe4, 0x29, 0x74, 0x0b, 0x71, 0x9c, 0xb6, 0x06, 0x13, 0x1f, 0x75, 0xf0, 0xcb,
0xb3, 0x4a, 0x90, 0xfb, 0x12, 0x7a, 0x95, 0x23, 0x49, 0x1f, 0xda, 0xdf, 0xd9, 0x02, 0xe9, 0x66,
0x9f, 0x64, 0x00, 0x5b, 0x67, 0xc1, 0x2c, 0xcd, 0xc9, 0x1a, 0xe3, 0x55, 0xeb, 0x85, 0xe5, 0xfd,
0x6c, 0x41, 0x9f, 0xa6, 0xfc, 0x44, 0x8b, 0x9e, 0xd3, 0x7d, 0xd3, 0xa0, 0xfb, 0xa0, 0x46, 0xb7,
0x86, 0xfd, 0xdf, 0x5c, 0xc9, 0x43, 0xb0, 0xcd, 0x84, 0x38, 0x1d, 0x0d, 0xdf, 0x29, 0xe0, 0x78,
0x2f, 0x0c, 0x6f, 0x22, 0xca, 0x2f, 0x23, 0x0a, 0xd5, 0x33, 0x77, 0x39, 0x51, 0x6a, 0xd8, 0x2b,
0x2b, 0x4a, 0x06, 0x34, 0x2f, 0xc9, 0xd9, 0x6a, 0x00, 0x91, 0x00, 0x86, 0x37, 0x51, 0xef, 0xb7,
0x05, 0xb7, 0xb2, 0x31, 0x09, 0xa2, 0xd9, 0x89, 0x79, 0x9b, 0xb9, 0x84, 0xc7, 0x0d, 0x09, 0x1f,
0xd7, 0xe7, 0xea, 0x42, 0xc2, 0x86, 0x3a, 0x3e, 0x87, 0x5e, 0x5c, 0xd6, 0x44, 0x25, 0xf7, 0x7c,
0xb3, 0x3c, 0xfc, 0xea, 0x71, 0x55, 0xdc, 0x26, 0xd4, 0x77, 0x61, 0xa7, 0xd8, 0x3e, 0x6a, 0x2e,
0xb8, 0x62, 0xde, 0x3b, 0x18, 0xd4, 0x57, 0x84, 0xf1, 0xd7, 0x9b, 0x6c, 0x5d, 0xa2, 0xc9, 0xde,
0x21, 0xec, 0x56, 0x5e, 0x1f, 0x96, 0x29, 0x3b, 0x6f, 0xad, 0xed, 0x3c, 0x66, 0xe7, 0x63, 0x5a,
0x66, 0xe3, 0x38, 0x58, 0x6b, 0xc7, 0xc1, 0x73, 0x60, 0xd8, 0xec, 0x10, 0xf2, 0x9b, 0x82, 0x6d,
0x64, 0xff, 0x97, 0x45, 0x4b, 0x1c, 0xb8, 0x96, 0x44, 0x31, 0x13, 0x69, 0xa2, 0xdb, 0x72, 0x83,
0xe6, 0x66, 0x26, 0x6e, 0xa0, 0x16, 0x3c, 0xd4, 0x93, 0xbc, 0x4d, 0x8d, 0x31, 0x39, 0x6f, 0xc1,
0x75, 0xec, 0x6e, 0xca, 0x39, 0x93, 0xe4, 0x35, 0x74, 0x32, 0x99, 0x89, 0x53, 0x1b, 0xa1, 0xca,
0xde, 0x77, 0xf7, 0x97, 0x44, 0x90, 0xf6, 0x7b, 0xe8, 0x16, 0x0a, 0x93, 0xbb, 0x6b, 0x77, 0xb9,
0x7b, 0x6f, 0x55, 0x18, 0x6b, 0x1d, 0x81, 0x6d, 0x94, 0x26, 0xee, 0xea, 0x2d, 0xe9, 0xde, 0x59,
0x1a, 0x2b, 0x4b, 0x18, 0xb9, 0x1b, 0x25, 0x6a, 0x3b, 0xa5, 0x51, 0xa2, 0xd1, 0xc8, 0x8f, 0xd0,
0xab, 0x34, 0x87, 0xdc, 0xff, 0xcb, 0xc3, 0x72, 0x47, 0xab, 0x01, 0xa6, 0xe2, 0x57, 0x5b, 0xff,
0x4f, 0x9f, 0xfd, 0x09, 0x00, 0x00, 0xff, 0xff, 0xef, 0xf0, 0x32, 0xb0, 0xd3, 0x07, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -540,6 +635,7 @@ type ScriptRunnerClient interface {
Namespace(ctx context.Context, in *RunNamespaceRequest, opts ...grpc.CallOption) (*RunNamespaceResponse, error)
Module(ctx context.Context, in *RunModuleRequest, opts ...grpc.CallOption) (*RunModuleResponse, error)
Record(ctx context.Context, in *RunRecordRequest, opts ...grpc.CallOption) (*RunRecordResponse, error)
MailMessage(ctx context.Context, in *RunMailMessageRequest, opts ...grpc.CallOption) (*RunMailMessageResponse, error)
}
type scriptRunnerClient struct {
@@ -586,12 +682,22 @@ func (c *scriptRunnerClient) Record(ctx context.Context, in *RunRecordRequest, o
return out, nil
}
func (c *scriptRunnerClient) MailMessage(ctx context.Context, in *RunMailMessageRequest, opts ...grpc.CallOption) (*RunMailMessageResponse, error) {
out := new(RunMailMessageResponse)
err := c.cc.Invoke(ctx, "/corredor.ScriptRunner/MailMessage", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ScriptRunnerServer is the server API for ScriptRunner service.
type ScriptRunnerServer interface {
Test(context.Context, *RunTestRequest) (*RunTestResponse, error)
Namespace(context.Context, *RunNamespaceRequest) (*RunNamespaceResponse, error)
Module(context.Context, *RunModuleRequest) (*RunModuleResponse, error)
Record(context.Context, *RunRecordRequest) (*RunRecordResponse, error)
MailMessage(context.Context, *RunMailMessageRequest) (*RunMailMessageResponse, error)
}
// UnimplementedScriptRunnerServer can be embedded to have forward compatible implementations.
@@ -610,6 +716,9 @@ func (*UnimplementedScriptRunnerServer) Module(ctx context.Context, req *RunModu
func (*UnimplementedScriptRunnerServer) Record(ctx context.Context, req *RunRecordRequest) (*RunRecordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Record not implemented")
}
func (*UnimplementedScriptRunnerServer) MailMessage(ctx context.Context, req *RunMailMessageRequest) (*RunMailMessageResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method MailMessage not implemented")
}
func RegisterScriptRunnerServer(s *grpc.Server, srv ScriptRunnerServer) {
s.RegisterService(&_ScriptRunner_serviceDesc, srv)
@@ -687,6 +796,24 @@ func _ScriptRunner_Record_Handler(srv interface{}, ctx context.Context, dec func
return interceptor(ctx, in, info, handler)
}
func _ScriptRunner_MailMessage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RunMailMessageRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ScriptRunnerServer).MailMessage(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/corredor.ScriptRunner/MailMessage",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ScriptRunnerServer).MailMessage(ctx, req.(*RunMailMessageRequest))
}
return interceptor(ctx, in, info, handler)
}
var _ScriptRunner_serviceDesc = grpc.ServiceDesc{
ServiceName: "corredor.ScriptRunner",
HandlerType: (*ScriptRunnerServer)(nil),
@@ -707,6 +834,10 @@ var _ScriptRunner_serviceDesc = grpc.ServiceDesc{
MethodName: "Record",
Handler: _ScriptRunner_Record_Handler,
},
{
MethodName: "MailMessage",
Handler: _ScriptRunner_MailMessage_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "service-corredor.proto",

View File

@@ -0,0 +1,196 @@
package mail
import (
"encoding/json"
"errors"
"net/mail"
"net/textproto"
"regexp"
"strings"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/system/types"
)
// Trigger condition:
// Matcher for mail headers
type (
Condition struct {
MatchAll bool `json:"matchAll"`
Headers []HeaderMatcher `json:"headers"`
}
HeaderMatcher struct {
Name HMName `json:"name"`
Op HMOp `json:"op"`
Match string `json:"match"`
// Compiled regexp
re *regexp.Regexp
}
HMName string
HMOp string
userExistanceVerifier func(string) bool
)
const (
HeaderMatchNameFrom HMName = "from"
HeaderMatchNameTo = "to"
HeaderMatchNameCC = "cc"
HeaderMatchNameBCC = "bcc"
HeaderMatchNameReplyTo = "reply-to"
HeaderMatchNameSubject = "subject"
// Keeping -ci suffix in case we get
// feature request to separete ci & cs operators
HMOpEqualCi HMOp = "equal-ci"
HMOpSuffixCi = "suffix-ci"
HMOpPrefixCi = "prefix-ci"
HMOpRegex = "regex"
HMOpUser = "user"
)
var (
ErrUnknownHeaderMatcherName = errors.New("unknown header matcher field")
ErrUnknownHeaderMatcherOperator = errors.New("unknown header matcher operator")
ErrInvalidHeaderMatcherValue = errors.New("invalid header matcher value")
)
func (c *Condition) Prepare() (err error) {
for i := range c.Headers {
err = c.Headers[i].prepare()
if err != nil {
return
}
}
return
}
// IsValid verifies if header matcher is valid
func (m *HeaderMatcher) prepare() (err error) {
switch m.Name {
case HeaderMatchNameFrom,
HeaderMatchNameTo,
HeaderMatchNameCC,
HeaderMatchNameBCC,
HeaderMatchNameReplyTo,
HeaderMatchNameSubject:
// ok fields
default:
return ErrUnknownHeaderMatcherName
}
switch m.Op {
case HMOpRegex:
// Try to compile given regex
m.re, err = regexp.Compile(m.Match)
if err != nil {
return ErrInvalidHeaderMatcherValue
}
case HMOpUser:
// When matching against existing user,
// there should be no value set
if m.Match != "" {
return ErrInvalidHeaderMatcherValue
}
case HMOpEqualCi, HMOpSuffixCi, HMOpPrefixCi:
// no special validation here
m.Match = strings.ToLower(m.Match)
default:
return ErrUnknownHeaderMatcherOperator
}
return
}
func (n HMName) match(name string) bool {
return string(n) == strings.ToLower(name)
}
// IsMatch checks if header matcher matches against given headers
func (m *HeaderMatcher) isMatch(header mail.Header, exists userExistanceVerifier, matchAll bool) (match bool) {
var lcHeader string
for name, vv := range header {
if !m.Name.match(name) {
continue
}
for _, v := range vv {
lcHeader = strings.ToLower(v)
switch m.Op {
case HMOpEqualCi:
match = m.Match == lcHeader
case HMOpSuffixCi:
match = strings.HasSuffix(lcHeader, m.Match)
case HMOpPrefixCi:
match = strings.HasPrefix(lcHeader, m.Match)
case HMOpRegex:
match = m.re.MatchString(v)
case HMOpUser:
match = exists(v)
default:
return false
}
if !match && matchAll {
// fail in first non-match
return false
} else if match && !matchAll {
// match in first
return true
}
}
}
return match
}
func (c *Condition) CheckHeader(header mail.Header, uev userExistanceVerifier) (match bool) {
_ = c.Prepare()
// Pre-process & simplify header values: parse all addresses,
// extract emails and toss away names, we do not need them
for name := range header {
switch textproto.CanonicalMIMEHeaderKey(name) {
case "From", "To", "Cc", "Bcc", "Reply-To":
for i, v := range header[name] {
addr, _ := mail.ParseAddress(v)
header[name][i] = strings.Trim(addr.Address, "><")
}
}
}
for _, h := range c.Headers {
match = h.isMatch(header, uev, c.MatchAll)
if !match && c.MatchAll {
// fail in first non-match
return false
} else if match && !c.MatchAll {
// match in first
return true
}
}
return match
}
func MakeChecker(headers types.MailMessageHeader, uev userExistanceVerifier) automation.TriggerConditionChecker {
return func(c string) bool {
var (
tc = Condition{}
)
if err := json.Unmarshal([]byte(c), &tc); err == nil {
return tc.CheckHeader(headers.Raw, uev)
}
return false
}
}

View File

@@ -0,0 +1,144 @@
package mail
import (
"encoding/json"
"testing"
"github.com/cortezaproject/corteza-server/system/types"
)
func Test_makeMailHeaderChecker(t *testing.T) {
tests := []struct {
name string
mh types.MailMessageHeader
tc Condition
expecting bool
}{
{
name: "empty should not match",
mh: types.MailMessageHeader{},
tc: Condition{},
},
{
name: "simple check",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "SIMPLE"}}},
expecting: true,
},
{
name: "simple check - no match",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "complex"}}},
expecting: false,
},
{
name: "simple check - no match",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameFrom, Op: HMOpEqualCi, Match: "SIMPLE"}}},
expecting: false,
},
{
name: "simple check - name-case",
mh: types.MailMessageHeader{Raw: map[string][]string{"SUBJECT": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "SIMPLE"}}},
expecting: true,
},
{
name: "check address (brackets)",
mh: types.MailMessageHeader{Raw: map[string][]string{"From": []string{"<some@mail.tld>"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameFrom, Op: HMOpEqualCi, Match: "some@mail.tld"}}},
expecting: true,
},
{
name: "check address (bare)",
mh: types.MailMessageHeader{Raw: map[string][]string{"From": []string{"some@mail.tld"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameFrom, Op: HMOpEqualCi, Match: "some@mail.tld"}}},
expecting: true,
},
{
name: "check address (full, quoted)",
mh: types.MailMessageHeader{Raw: map[string][]string{"From": []string{`"John Doe" <some@mail.tld>`}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameFrom, Op: HMOpEqualCi, Match: "some@mail.tld"}}},
expecting: true,
},
{
name: "check address (full)",
mh: types.MailMessageHeader{Raw: map[string][]string{"From": []string{`John Doe <some@mail.tld>`}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameFrom, Op: HMOpEqualCi, Match: "some@mail.tld"}}},
expecting: true,
},
{
name: "two matchers, one matches",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{
Headers: []HeaderMatcher{
{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "SIMPLE"},
{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "complex"},
},
},
expecting: true,
},
{
name: "two matchers, one matches, match-all=true",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{
MatchAll: true,
Headers: []HeaderMatcher{
{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "SIMPLE"},
{Name: HeaderMatchNameSubject, Op: HMOpEqualCi, Match: "complex"},
},
},
expecting: false,
},
{
name: "match by prefix",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"abcd"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpPrefixCi, Match: "ab"}}},
expecting: true,
},
{
name: "match by prefix",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"abcd"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpPrefixCi, Match: "cd"}}},
expecting: false,
},
{
name: "match by suffix",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"abcd"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpSuffixCi, Match: "cd"}}},
expecting: true,
},
{
name: "match by suffix",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"abcd"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Op: HMOpSuffixCi, Match: "ab"}}},
expecting: false,
},
{
name: "regex check",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Match: "^S.+$", Op: HMOpRegex}}},
expecting: true,
},
{
name: "case-insensitive check",
mh: types.MailMessageHeader{Raw: map[string][]string{"Subject": []string{"SIMPLE"}}},
tc: Condition{Headers: []HeaderMatcher{{Name: HeaderMatchNameSubject, Match: "simple", Op: HMOpEqualCi}}},
expecting: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.tc.Prepare(); err != nil {
t.Errorf("unable to prepare header matcher: %v", err)
}
checker := MakeChecker(tt.mh, nil)
j, _ := json.Marshal(tt.tc)
if checker(string(j)) != tt.expecting {
t.Errorf("did not match (expecting: %v)", tt.expecting)
}
})
}
}

89
system/commands/sink.go Normal file
View File

@@ -0,0 +1,89 @@
package commands
import (
"context"
"net/url"
"strings"
"time"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/pkg/cli"
)
// Will perform OpenID connect auto-configuration
func Sink(ctx context.Context, c *cli.Config) *cobra.Command {
var (
expires string
origin string
contentType string
method string
)
cmd := &cobra.Command{
Use: "sink",
Short: "Sink",
}
signatureCmd := &cobra.Command{
Use: "signature",
Short: "Creates signature for sink HTTP endpoint",
RunE: func(cmd *cobra.Command, args []string) error {
c.InitServices(ctx, c)
method = strings.ToUpper(method)
if expires != "" {
// validate expiration date if set
if _, err := time.Parse("2006-01-02", expires); err != nil {
return err
}
}
v := url.Values{}
v.Set("sign", auth.DefaultSigner.Sign(0, method, "/sink", contentType, origin, expires))
v.Set("expires", expires)
v.Set("content-type", contentType)
v.Set("origin", origin)
v.Set("method", method)
// @todo add host & schema
cmd.Println((&url.URL{
Path: "/sink",
RawQuery: v.Encode()}).String())
return nil
},
}
signatureCmd.Flags().StringVar(
&origin,
"origin",
"",
"Origin of the request (arbitrary string, optional)")
signatureCmd.Flags().StringVar(
&contentType,
"content-type",
"",
"Content type (optional)")
signatureCmd.Flags().StringVar(
&expires,
"expires",
"",
"Date of expiration (YYYY-MM-DD, optional)")
signatureCmd.Flags().StringVar(
&method,
"method",
"GET",
"HTTP method that will be used")
cmd.AddCommand(
signatureCmd,
)
return cmd
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
CREATE TABLE IF NOT EXISTS sys_automation_script (
`id` BIGINT(20) UNSIGNED NOT NULL,
`rel_namespace` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'For compatibility only, not used',
`name` VARCHAR(64) NOT NULL DEFAULT 'unnamed' COMMENT 'The name of the script',
`source` TEXT NOT NULL COMMENT 'Source code for the script',
`source_ref` VARCHAR(200) NOT NULL COMMENT 'Where is the script located (if remote)',
`async` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'Do we run this script asynchronously?',
`rel_runner` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Who is running the script? 0 for invoker',
`run_in_ua` BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'Run this script inside user-agent environment',
`timeout` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'Any explicit timeout set for this script (milliseconds)?',
`critical` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'Is it critical that this script is executed successfully',
`enabled` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'Is this script enabled?',
`created_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NULL DEFAULT NULL,
`deleted_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS sys_automation_trigger (
`id` BIGINT(20) UNSIGNED NOT NULL,
`rel_script` BIGINT(20) UNSIGNED NOT NULL COMMENT 'Script that is triggered',
`resource` VARCHAR(128) NOT NULL COMMENT 'Resource triggering the event',
`event` VARCHAR(128) NOT NULL COMMENT 'Event triggered',
`event_condition`
TEXT NOT NULL COMMENT 'Trigger condition',
`enabled` BOOLEAN NOT NULL DEFAULT TRUE COMMENT 'Trigger enabled?',
`weight` INT NOT NULL DEFAULT 0,
`created_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NULL DEFAULT NULL,
`deleted_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`deleted_at` DATETIME NULL DEFAULT NULL,
CONSTRAINT `fk_sys_automation_script` FOREIGN KEY (`rel_script`) REFERENCES `sys_automation_script` (`id`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

View File

@@ -4,6 +4,7 @@ import (
"context"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/system/types"
)
@@ -76,6 +77,10 @@ func (svc accessControl) CanCreateApplication(ctx context.Context) bool {
return svc.can(ctx, types.SystemPermissionResource, "application.create")
}
func (svc accessControl) CanCreateAutomationScript(ctx context.Context) bool {
return svc.can(ctx, types.SystemPermissionResource, "automation-script.create")
}
func (svc accessControl) CanReadRole(ctx context.Context, rl *types.Role) bool {
return svc.can(ctx, rl, "read", permissions.Allowed)
}
@@ -120,6 +125,26 @@ func (svc accessControl) CanDeleteUser(ctx context.Context, u *types.User) bool
return svc.can(ctx, u, "delete")
}
func (svc accessControl) CanReadAnyAutomationScript(ctx context.Context) bool {
return svc.can(ctx, types.AutomationScriptPermissionResource.AppendWildcard(), "read")
}
func (svc accessControl) CanReadAutomationScript(ctx context.Context, r *automation.Script) bool {
return svc.can(ctx, types.AutomationScriptPermissionResource.AppendID(r.ID), "read")
}
func (svc accessControl) CanUpdateAutomationScript(ctx context.Context, r *automation.Script) bool {
return svc.can(ctx, types.AutomationScriptPermissionResource.AppendID(r.ID), "update")
}
func (svc accessControl) CanDeleteAutomationScript(ctx context.Context, r *automation.Script) bool {
return svc.can(ctx, types.AutomationScriptPermissionResource.AppendID(r.ID), "delete")
}
func (svc accessControl) CanRunAutomationTrigger(ctx context.Context, r *automation.Trigger) bool {
return svc.can(ctx, types.AutomationScriptPermissionResource.AppendID(r.ID), "run", permissions.Allowed)
}
func (svc accessControl) can(ctx context.Context, res permissionResource, op permissions.Operation, ff ...permissions.CheckAccessFunc) bool {
return svc.permissions.Can(ctx, res.PermissionResource(), op, ff...)
}
@@ -148,6 +173,7 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
organisations = types.OrganisationPermissionResource.AppendWildcard()
roles = types.RolePermissionResource.AppendWildcard()
users = types.UserPermissionResource.AppendWildcard()
ascripts = types.AutomationScriptPermissionResource.AppendWildcard()
allowAdm = func(res permissions.Resource, op permissions.Operation) *permissions.Rule {
return permissions.AllowRule(permissions.AdminRoleID, res, op)
@@ -165,6 +191,7 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
allowAdm(sys, "application.create"),
allowAdm(sys, "user.create"),
allowAdm(sys, "role.create"),
allowAdm(sys, "automation-script.create"),
allowAdm(organisations, "access"),
allowAdm(applications, "read"),
@@ -181,6 +208,10 @@ func (svc accessControl) DefaultRules() permissions.RuleSet {
allowAdm(roles, "update"),
allowAdm(roles, "delete"),
allowAdm(roles, "members.manage"),
allowAdm(ascripts, "read"),
allowAdm(ascripts, "update"),
allowAdm(ascripts, "delete"),
}
}
@@ -197,6 +228,7 @@ func (svc accessControl) Whitelist() permissions.Whitelist {
"role.create",
"user.create",
"application.create",
"automation-script.create",
)
wl.Set(
@@ -228,5 +260,17 @@ func (svc accessControl) Whitelist() permissions.Whitelist {
"members.manage",
)
wl.Set(
types.AutomationScriptPermissionResource,
"read",
"update",
"delete",
)
wl.Set(
types.AutomationTriggerPermissionResource,
"run",
)
return wl
}

View File

@@ -0,0 +1,193 @@
package service
import (
"context"
"time"
"github.com/pkg/errors"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
intAuth "github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/pkg/automation/corredor"
mailTrigger "github.com/cortezaproject/corteza-server/pkg/automation/mail"
"github.com/cortezaproject/corteza-server/pkg/sentry"
"github.com/cortezaproject/corteza-server/system/internal/repository"
"github.com/cortezaproject/corteza-server/system/proto"
"github.com/cortezaproject/corteza-server/system/types"
)
type (
automationRunner struct {
opt AutomationRunnerOpt
logger *zap.Logger
runner corredor.ScriptRunnerClient
userFinder automationRunnerUserFinder
scriptFinder automationScriptsFinder
jwtEncoder intAuth.TokenEncoder
}
automationRunnerUserFinder interface {
FindByEmail(string) (*types.User, error)
}
automationScriptsFinder interface {
Watch(ctx context.Context)
FindRunnableScripts(resource, event string, cc ...automation.TriggerConditionChecker) automation.ScriptSet
}
AutomationRunnerOpt struct {
ApiBaseURLSystem string
ApiBaseURLMessaging string
ApiBaseURLCompose string
}
)
func AutomationRunner(opt AutomationRunnerOpt, f automationScriptsFinder, r corredor.ScriptRunnerClient) automationRunner {
var svc = automationRunner{
opt: opt,
userFinder: DefaultUser,
scriptFinder: f,
runner: r,
logger: DefaultLogger.Named("automationRunner"),
jwtEncoder: intAuth.DefaultJwtHandler,
}
return svc
}
func (svc automationRunner) Watch(ctx context.Context) {
svc.scriptFinder.Watch(ctx)
}
func (svc automationRunner) OnReceiveMailMessage(ctx context.Context, mail *types.MailMessage) error {
return svc.findMailScripts(mail.Header).Walk(
svc.makeMailScriptRunner(ctx, mail),
)
}
// Finds all scripts that can process email
func (svc automationRunner) findMailScripts(headers types.MailMessageHeader) automation.ScriptSet {
uev := func(email string) bool {
u, err := svc.userFinder.FindByEmail(email)
return u != nil && err == nil
}
ss, _ := svc.scriptFinder.
FindRunnableScripts("system:mail", "onReceive", mailTrigger.MakeChecker(headers, uev)).
Filter(func(script *automation.Script) (bool, error) {
// Filter out user-agent scripts && scripts w/o defined runner.
return !script.RunInUA && script.RunAsDefined(), nil
})
return ss
}
func (svc automationRunner) RecordScriptTester(ctx context.Context, source string, payload interface{}) (err error) {
// Make record script runner
// @todo figure out how to convert payload to *types.MailMessage
// runner := svc.makeMailScriptRunner(ctx, payload)
//
// return runner(&automation.Script{
// ID: 0,
// Name: "test",
// SourceRef: "test",
// Source: source,
// Async: false,
// RunAs: 0,
// RunInUA: false,
// Timeout: 0,
// Critical: true,
// Enabled: false,
// })
return repository.ErrNotImplemented
}
// Runs record script
//
// We set-up script-running environment: security (definer / invoker), async, critical
// and copying values from the run to the given Record
//
func (svc automationRunner) makeMailScriptRunner(ctx context.Context, mail *types.MailMessage) func(script *automation.Script) error {
// Static request params (record gets updated
var req = &corredor.RunMailMessageRequest{
MailMessage: proto.NewMailMessage(mail),
}
svc.logger.Debug("preparing mail script runner", zap.Any("mail", mail))
return func(script *automation.Script) error {
if svc.runner == nil {
return errors.New("can not run corredor script: not connected")
}
// This could be executed in a goroutine (by *after triggers,
// so we need to rewire the sentry panic recovery
defer sentry.Recover()
ctx, cancelFn := context.WithTimeout(ctx, time.Second*5)
defer cancelFn()
// Add invoker's or defined credentials/jwt
req.Config = map[string]string{
"api.jwt": svc.getJWT(ctx, script),
// Let the script know where the API is
"api.baseURL.system": svc.opt.ApiBaseURLSystem,
"api.baseURL.compose": svc.opt.ApiBaseURLCompose,
"api.baseURL.messaging": svc.opt.ApiBaseURLMessaging,
}
// Add script info
req.Script = corredor.FromScript(script)
_, err := svc.runner.MailMessage(ctx, req, grpc.WaitForReady(script.Critical))
if err != nil {
s, ok := status.FromError(err)
if !ok {
svc.logger.Error("unexpected error type", zap.Error(err))
return err
}
switch s.Code() {
case codes.FailedPrecondition:
// Sent on syntax errors:
err = errors.New(s.Message())
case codes.Aborted:
err = errors.New(s.Message())
case codes.InvalidArgument:
err = errors.New("invalid argument")
case codes.Internal:
err = errors.New("internal corredor error")
default:
}
svc.logger.Info("script executed with errors", zap.Error(err))
if !script.Critical {
// This was not a critical call and we do not care about
// errors from script running service.
return nil
}
return err
}
return nil
}
}
// Creates a new JWT for
func (svc automationRunner) getJWT(ctx context.Context, script *automation.Script) string {
if script.RunAsDefined() {
return script.Credentials()
}
return svc.jwtEncoder.Encode(intAuth.GetIdentityFromContext(ctx))
}

View File

@@ -0,0 +1,158 @@
package service
import (
"context"
"go.uber.org/zap"
intAuth "github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/permissions"
"github.com/cortezaproject/corteza-server/pkg/automation"
)
type (
automationScript struct {
logger *zap.Logger
scriptManager automationScriptManager
ac automationScriptAccessController
trg automationTrigger
}
automationScriptManager interface {
FindScriptByID(context.Context, uint64) (*automation.Script, error)
FindScripts(context.Context, automation.ScriptFilter) (automation.ScriptSet, automation.ScriptFilter, error)
CreateScript(context.Context, *automation.Script) error
UpdateScript(context.Context, *automation.Script) error
DeleteScript(context.Context, *automation.Script) error
}
automationScriptAccessController interface {
CanGrant(context.Context) bool
CanCreateAutomationScript(context.Context) bool
CanReadAnyAutomationScript(context.Context) bool
CanReadAutomationScript(context.Context, *automation.Script) bool
CanUpdateAutomationScript(context.Context, *automation.Script) bool
CanDeleteAutomationScript(context.Context, *automation.Script) bool
}
)
func AutomationScript(sm automationScriptManager) automationScript {
var svc = automationScript{
scriptManager: sm,
logger: DefaultLogger.Named("automation-script"),
ac: DefaultAccessControl,
trg: DefaultAutomationTriggerManager,
}
return svc
}
func (svc automationScript) FindByID(ctx context.Context, scriptID uint64) (*automation.Script, error) {
if s, err := svc.loadCombo(ctx, scriptID); err != nil {
return nil, err
} else {
return s, nil
}
}
func (svc automationScript) Find(ctx context.Context, f automation.ScriptFilter) (automation.ScriptSet, automation.ScriptFilter, error) {
f.AccessCheck = permissions.InitAccessCheckFilter(
"read",
intAuth.GetIdentityFromContext(ctx).Roles(),
svc.ac.CanReadAnyAutomationScript(ctx),
)
return svc.scriptManager.FindScripts(ctx, f)
}
func (svc automationScript) Create(ctx context.Context, mod *automation.Script) (err error) {
if !svc.ac.CanCreateAutomationScript(ctx) {
return ErrNoCreatePermissions.withStack()
}
if mod.RunAs > 0 {
if !svc.ac.CanGrant(ctx) {
return ErrNoGrantPermissions
}
}
err = mod.Triggers().Walk(func(t *automation.Trigger) error {
return svc.trg.isValid(ctx, mod, t)
})
if err != nil {
return
}
return svc.scriptManager.CreateScript(ctx, mod)
}
func (svc automationScript) Update(ctx context.Context, mod *automation.Script) (err error) {
var s *automation.Script
if s, err = svc.loadCombo(ctx, mod.ID); err != nil {
return err
}
if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoCreatePermissions.withStack()
}
// Users need to have grant privileges to
// set script runner
if mod.RunAs != s.RunAs {
if !svc.ac.CanGrant(ctx) {
return ErrNoGrantPermissions
}
}
s.Name = mod.Name
s.SourceRef = mod.SourceRef
s.Source = mod.Source
s.Async = mod.Async
s.RunAs = mod.RunAs
s.Timeout = mod.Timeout
s.Critical = mod.Critical
s.Enabled = mod.Enabled
// Never run in a UA
s.RunInUA = false
err = mod.Triggers().Walk(func(t *automation.Trigger) error {
return svc.trg.isValid(ctx, mod, t)
})
if err != nil {
return
}
s.AddTrigger(automation.STMS_UPDATE, mod.Triggers()...)
return svc.scriptManager.UpdateScript(ctx, s)
}
func (svc automationScript) Delete(ctx context.Context, scriptID uint64) (err error) {
if s, err := svc.loadCombo(ctx, scriptID); err != nil {
return err
} else if !svc.ac.CanDeleteAutomationScript(ctx, s) {
return ErrNoCreatePermissions.withStack()
} else {
return svc.scriptManager.DeleteScript(ctx, s)
}
}
func (svc automationScript) loadCombo(ctx context.Context, scriptID uint64) (s *automation.Script, err error) {
if scriptID > 0 {
if s, err = svc.scriptManager.FindScriptByID(ctx, scriptID); err != nil {
return
} else if !svc.ac.CanReadAutomationScript(ctx, s) {
err = ErrNoCreatePermissions.withStack()
return
}
}
return
}

View File

@@ -0,0 +1,120 @@
package service
import (
"context"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/pkg/automation"
)
type (
// Handles automation triggers storing and loading
automationTrigger struct {
logger *zap.Logger
triggerManager automationTriggerManager
ac automationTriggerAccessController
}
automationTriggerManager interface {
FindTriggerByID(context.Context, uint64) (*automation.Trigger, error)
FindTriggers(context.Context, automation.TriggerFilter) (automation.TriggerSet, automation.TriggerFilter, error)
CreateTrigger(context.Context, *automation.Script, *automation.Trigger) error
UpdateTrigger(context.Context, *automation.Script, *automation.Trigger) error
DeleteTrigger(context.Context, *automation.Trigger) error
}
automationTriggerAccessController interface {
CanCreateAutomationScript(context.Context) bool
CanUpdateAutomationScript(context.Context, *automation.Script) bool
}
)
func AutomationTrigger(tm automationTriggerManager) automationTrigger {
var svc = automationTrigger{
triggerManager: tm,
logger: DefaultLogger.Named("automation-trigger"),
ac: DefaultAccessControl,
}
return svc
}
func (svc automationTrigger) FindByID(ctx context.Context, triggerID uint64) (*automation.Trigger, error) {
// @todo security check - can user read this trigger?
return svc.triggerManager.FindTriggerByID(ctx, triggerID)
}
func (svc automationTrigger) Find(ctx context.Context, f automation.TriggerFilter) (automation.TriggerSet, automation.TriggerFilter, error) {
// @todo security check - can user read these triggers?
return svc.triggerManager.FindTriggers(ctx, f)
}
func (svc automationTrigger) Create(ctx context.Context, s *automation.Script, t *automation.Trigger) (err error) {
if err = svc.isValid(ctx, s, t); err != nil {
return
}
if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoTriggerManagementPermissions
}
return svc.triggerManager.CreateTrigger(ctx, s, t)
}
func (svc automationTrigger) Update(ctx context.Context, s *automation.Script, t *automation.Trigger) (err error) {
if err = svc.isValid(ctx, s, t); err != nil {
return
}
if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoTriggerManagementPermissions
}
return svc.triggerManager.UpdateTrigger(ctx, s, t)
}
func (svc automationTrigger) Delete(ctx context.Context, s *automation.Script, t *automation.Trigger) (err error) {
if err = svc.isValid(ctx, s, t); err != nil {
return
}
if !svc.ac.CanUpdateAutomationScript(ctx, s) {
return ErrNoTriggerManagementPermissions
}
return svc.triggerManager.DeleteTrigger(ctx, t)
}
// Validates trigger (in compose context)
func (svc automationTrigger) isValid(ctx context.Context, s *automation.Script, t *automation.Trigger) error {
if !t.Enabled {
return nil
}
if t.Resource == "system:mail" {
if !svc.ac.CanCreateAutomationScript(ctx) {
return ErrNoScriptCreatePermissions
}
if t.IsDeferred() {
// @todo validate condition for deferred triggers
return automation.ErrAutomationTriggerInvalidEvent
}
switch t.Event {
case "onReceive":
// @todo validate
default:
return automation.ErrAutomationTriggerInvalidEvent
}
return nil
}
return automation.ErrAutomationTriggerInvalidResource
}

View File

@@ -9,8 +9,13 @@ type (
)
const (
ErrInvalidID serviceError = "InvalidID"
ErrNoPermissions serviceError = "NoPermissions"
ErrInvalidID serviceError = "InvalidID"
ErrNoPermissions serviceError = "NoPermissions"
ErrNoGrantPermissions serviceError = "NoGrantPermissions"
ErrNoCreatePermissions serviceError = "NoCreatePermissions"
ErrNoReadPermissions serviceError = "NoReadPermissions"
ErrNoTriggerManagementPermissions serviceError = "NoTriggerManagementPermissions"
ErrNoScriptCreatePermissions serviceError = "NoScriptCreatePermissions"
)
func (e serviceError) Error() string {

View File

@@ -0,0 +1,100 @@
package service
import (
"context"
"io"
"io/ioutil"
"net/mail"
"go.uber.org/zap"
"github.com/cortezaproject/corteza-server/system/types"
)
type (
mailproc struct {
sr mailprocScriptsRunner
logger *zap.Logger
}
mailprocScriptsRunner interface {
OnReceiveMailMessage(ctx context.Context, message *types.MailMessage) (err error)
}
)
func Mailproc() *mailproc {
return &mailproc{
sr: DefaultAutomationRunner,
logger: DefaultLogger.Named("mailproc"),
}
}
// log() returns zap's logger with requestID from current context and fields.
// func (svc mailproc) log(fields ...zapcore.Field) *zap.Logger {
// return logger.AddRequestID(svc.ctx, svc.logger).With(fields...)
// }
func (svc mailproc) ContentProcessor(ctx context.Context, m io.Reader) error {
if m, err := mailProcMessage(m); err != nil {
return err
} else if err = svc.sr.OnReceiveMailMessage(ctx, m); err != nil {
return err
}
return nil
}
func mailProcMessage(r io.Reader) (out *types.MailMessage, err error) {
var (
aa []*mail.Address
msg *mail.Message
addrKeys = []string{
"from",
"to",
"cc",
"bcc",
"reply-to",
}
)
if msg, err = mail.ReadMessage(r); err != nil {
return
}
out = &types.MailMessage{}
out.Header.Raw = msg.Header
out.Date, _ = msg.Header.Date()
for _, key := range addrKeys {
aa, err = msg.Header.AddressList(key)
if err != nil && err != mail.ErrHeaderNotPresent {
return
}
if len(aa) > 0 {
switch key {
case "from":
out.Header.From = aa
case "to":
out.Header.To = aa
case "cc":
out.Header.CC = aa
case "bcc":
out.Header.BCC = aa
case "reply-to":
out.Header.ReplyTo = aa
}
}
}
if out.RawBody, err = ioutil.ReadAll(msg.Body); err != nil {
return
}
return
}

View File

@@ -0,0 +1,61 @@
package service
import (
"net/mail"
"reflect"
"strings"
"testing"
"time"
"github.com/cortezaproject/corteza-server/system/types"
)
func Test_mailProcMessage(t *testing.T) {
tests := []struct {
name string
input string
wantOut *types.MailMessage
wantErr bool
}{
// TODO: Add test cases.
{name: "basics",
input: `
From: <sender@testing.cortezaproject.org>
To: <rcpt@testing.cortezaproject.org>
Subject: Customer service contact info
Message-ID: <1234@local.machine.example>
Ola Corteza!
`,
wantOut: &types.MailMessage{
Date: time.Time{},
Header: types.MailMessageHeader{
From: []*mail.Address{{Address: "sender@testing.cortezaproject.org"}},
To: []*mail.Address{{Address: "rcpt@testing.cortezaproject.org"}},
Raw: map[string][]string{
"From": []string{"<sender@testing.cortezaproject.org>"},
"To": []string{"<rcpt@testing.cortezaproject.org>"},
"Subject": []string{"Customer service contact info"},
"Message-Id": []string{"<1234@local.machine.example>"},
},
},
RawBody: []byte(`Ola Corteza!`),
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
input := strings.NewReader(strings.TrimSpace(tt.input))
gotOut, err := mailProcMessage(input)
if (err != nil) != tt.wantErr {
t.Errorf("mailProcMessage() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotOut, tt.wantOut) {
t.Errorf("mailProcMessage() \ngotOut: %v, \n want: %v", gotOut, tt.wantOut)
}
})
}
}

View File

@@ -5,9 +5,14 @@ import (
"go.uber.org/zap"
intAuth "github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/permissions"
internalSettings "github.com/cortezaproject/corteza-server/internal/settings"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/pkg/automation/corredor"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
"github.com/cortezaproject/corteza-server/system/internal/repository"
"github.com/cortezaproject/corteza-server/system/types"
)
type (
@@ -18,20 +23,40 @@ type (
accessControlPermissionServicer
Watch(ctx context.Context)
}
Config struct {
Storage options.StorageOpt
Corredor options.CorredorOpt
GRPCClientSystem options.GRPCServerOpt
}
)
var (
DefaultPermissions permissionServicer
DefaultIntSettings internalSettings.Service
DefaultLogger *zap.Logger
// DefaultPermissions Retrieves & stores permissions
DefaultPermissions permissionServicer
DefaultIntSettings internalSettings.Service
DefaultSettings SettingsService
// DefaultAccessControl Access control checking
DefaultAccessControl *accessControl
DefaultSettings SettingsService
// DefaultAutomationScriptManager manages scripts
DefaultAutomationScriptManager automationScript
// DefaultAutomationTriggerManager manages triggerManager
DefaultAutomationTriggerManager automationTrigger
// DefaultAutomationRunner runs automation scripts by listening to triggerManager and invoking Corredor service
DefaultAutomationRunner automationRunner
DefaultAuthNotification AuthNotificationService
DefaultAuthSettings AuthSettings
DefaultSink *sink
DefaultAuth AuthService
DefaultUser UserService
DefaultRole RoleService
@@ -39,7 +64,7 @@ var (
DefaultApplication ApplicationService
)
func Init(ctx context.Context, log *zap.Logger) (err error) {
func Init(ctx context.Context, log *zap.Logger, c Config) (err error) {
DefaultLogger = log.Named("service")
DefaultIntSettings = internalSettings.NewService(internalSettings.NewRepository(repository.DB(ctx), "sys_settings"))
@@ -65,9 +90,64 @@ func Init(ctx context.Context, log *zap.Logger) (err error) {
DefaultAuthNotification = AuthNotification(ctx)
DefaultAuth = Auth(ctx)
// ias: Internal Automatinon Service
// handles script & trigger management & keeping runnables cripts in internal cache
ias := automation.Service(automation.AutomationServiceConfig{
Logger: DefaultLogger,
DbTablePrefix: "sys",
DB: repository.DB(ctx),
TokenMaker: func(ctx context.Context, userID uint64) (jwt string, err error) {
var u *types.User
ctx = intAuth.SetSuperUserContext(ctx)
if u, err = DefaultUser.FindByID(userID); err != nil {
return
} else if err = DefaultAuth.LoadRoleMemberships(u); err != nil {
return
}
return intAuth.DefaultJwtHandler.Encode(u), nil
},
})
// Pass automation manager to
DefaultAutomationTriggerManager = AutomationTrigger(ias)
DefaultAutomationScriptManager = AutomationScript(ias)
{
var scriptRunnerClient corredor.ScriptRunnerClient
if c.Corredor.Enabled {
conn, err := corredor.NewConnection(ctx, c.Corredor, DefaultLogger)
log.Info("initializing corredor connection", zap.String("addr", c.Corredor.Addr), zap.Error(err))
if err != nil {
return err
}
scriptRunnerClient = corredor.NewScriptRunnerClient(conn)
}
DefaultAutomationRunner = AutomationRunner(
AutomationRunnerOpt{
ApiBaseURLSystem: c.Corredor.ApiBaseURLSystem,
ApiBaseURLMessaging: c.Corredor.ApiBaseURLMessaging,
ApiBaseURLCompose: c.Corredor.ApiBaseURLCompose,
},
ias,
scriptRunnerClient,
)
}
DefaultSink = Sink()
return
}
func Watchers(ctx context.Context) {
// Reloading automation scripts on change
DefaultAutomationRunner.Watch(ctx)
// Reloading permissions on change
DefaultPermissions.Watch(ctx)
}

View File

@@ -0,0 +1,53 @@
package service
import (
"context"
"io"
"strings"
"go.uber.org/zap"
)
type (
sink struct {
// processors
proc map[string]sinkContentProc
logger *zap.Logger
}
sinkContentProc interface {
ContentProcessor(context.Context, io.Reader) error
}
)
const (
ErrSinkContentTypeUnsupported serviceError = "SinkUnsupportedContentType"
ErrSinkContentProcessingFailed serviceError = "SinkProcessFailed"
SinkContentTypeMail = "message/rfc822"
)
func Sink() *sink {
return &sink{
logger: DefaultLogger,
proc: map[string]sinkContentProc{
SinkContentTypeMail: Mailproc(),
},
}
}
// Finds appropriate sink processor
func (svc *sink) Process(ctx context.Context, contentType string, r io.Reader) (err error) {
switch strings.ToLower(contentType) {
case SinkContentTypeMail, "rfc822", "email", "mail":
if err = svc.proc[SinkContentTypeMail].ContentProcessor(ctx, r); err != nil {
return ErrSinkContentProcessingFailed
}
default:
return ErrSinkContentTypeUnsupported
}
return
}

View File

@@ -0,0 +1,53 @@
package proto
import (
mail2 "net/mail"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/cortezaproject/corteza-server/system/types"
)
func NewMailMessage(mail *types.MailMessage) *MailMessage {
if mail == nil {
return nil
}
addrConv := func(aa []*mail2.Address) []*MailMessage_Header_MailAddress {
out := make([]*MailMessage_Header_MailAddress, len(aa))
for i, a := range aa {
out[i] = &MailMessage_Header_MailAddress{
Address: a.Address,
Name: a.Name,
}
}
return out
}
hConv := func(hh mail2.Header) map[string]*MailMessage_Header_HeaderValues {
out := make(map[string]*MailMessage_Header_HeaderValues, len(hh))
for k, vv := range hh {
out[k] = &MailMessage_Header_HeaderValues{Values: vv}
}
return out
}
var p = &MailMessage{
Header: &MailMessage_Header{
Date: &timestamp.Timestamp{Seconds: mail.Date.Unix()},
To: addrConv(mail.Header.To),
Cc: addrConv(mail.Header.CC),
Bcc: addrConv(mail.Header.BCC),
From: addrConv(mail.Header.From),
ReplyTo: addrConv(mail.Header.ReplyTo),
Raw: hConv(mail.Header.Raw),
},
RawBody: mail.RawBody,
}
return p
}

View File

@@ -0,0 +1,286 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: mail_message.proto
package proto
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type MailMessage struct {
Header *MailMessage_Header `protobuf:"bytes,6,opt,name=header,proto3" json:"header,omitempty"`
RawBody []byte `protobuf:"bytes,15,opt,name=rawBody,proto3" json:"rawBody,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MailMessage) Reset() { *m = MailMessage{} }
func (m *MailMessage) String() string { return proto.CompactTextString(m) }
func (*MailMessage) ProtoMessage() {}
func (*MailMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_d142ecb16d253662, []int{0}
}
func (m *MailMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MailMessage.Unmarshal(m, b)
}
func (m *MailMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MailMessage.Marshal(b, m, deterministic)
}
func (m *MailMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_MailMessage.Merge(m, src)
}
func (m *MailMessage) XXX_Size() int {
return xxx_messageInfo_MailMessage.Size(m)
}
func (m *MailMessage) XXX_DiscardUnknown() {
xxx_messageInfo_MailMessage.DiscardUnknown(m)
}
var xxx_messageInfo_MailMessage proto.InternalMessageInfo
func (m *MailMessage) GetHeader() *MailMessage_Header {
if m != nil {
return m.Header
}
return nil
}
func (m *MailMessage) GetRawBody() []byte {
if m != nil {
return m.RawBody
}
return nil
}
// All typified data (date, to, cc, bcc...)
// is extracted and parsed
//
// Simple string values (subject, message-id, ...)
// are kept under Raw
//
type MailMessage_Header struct {
Date *timestamp.Timestamp `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"`
To []*MailMessage_Header_MailAddress `protobuf:"bytes,2,rep,name=to,proto3" json:"to,omitempty"`
Cc []*MailMessage_Header_MailAddress `protobuf:"bytes,3,rep,name=cc,proto3" json:"cc,omitempty"`
Bcc []*MailMessage_Header_MailAddress `protobuf:"bytes,4,rep,name=bcc,proto3" json:"bcc,omitempty"`
From []*MailMessage_Header_MailAddress `protobuf:"bytes,5,rep,name=from,proto3" json:"from,omitempty"`
ReplyTo []*MailMessage_Header_MailAddress `protobuf:"bytes,6,rep,name=replyTo,proto3" json:"replyTo,omitempty"`
Raw map[string]*MailMessage_Header_HeaderValues `protobuf:"bytes,15,rep,name=raw,proto3" json:"raw,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MailMessage_Header) Reset() { *m = MailMessage_Header{} }
func (m *MailMessage_Header) String() string { return proto.CompactTextString(m) }
func (*MailMessage_Header) ProtoMessage() {}
func (*MailMessage_Header) Descriptor() ([]byte, []int) {
return fileDescriptor_d142ecb16d253662, []int{0, 0}
}
func (m *MailMessage_Header) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MailMessage_Header.Unmarshal(m, b)
}
func (m *MailMessage_Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MailMessage_Header.Marshal(b, m, deterministic)
}
func (m *MailMessage_Header) XXX_Merge(src proto.Message) {
xxx_messageInfo_MailMessage_Header.Merge(m, src)
}
func (m *MailMessage_Header) XXX_Size() int {
return xxx_messageInfo_MailMessage_Header.Size(m)
}
func (m *MailMessage_Header) XXX_DiscardUnknown() {
xxx_messageInfo_MailMessage_Header.DiscardUnknown(m)
}
var xxx_messageInfo_MailMessage_Header proto.InternalMessageInfo
func (m *MailMessage_Header) GetDate() *timestamp.Timestamp {
if m != nil {
return m.Date
}
return nil
}
func (m *MailMessage_Header) GetTo() []*MailMessage_Header_MailAddress {
if m != nil {
return m.To
}
return nil
}
func (m *MailMessage_Header) GetCc() []*MailMessage_Header_MailAddress {
if m != nil {
return m.Cc
}
return nil
}
func (m *MailMessage_Header) GetBcc() []*MailMessage_Header_MailAddress {
if m != nil {
return m.Bcc
}
return nil
}
func (m *MailMessage_Header) GetFrom() []*MailMessage_Header_MailAddress {
if m != nil {
return m.From
}
return nil
}
func (m *MailMessage_Header) GetReplyTo() []*MailMessage_Header_MailAddress {
if m != nil {
return m.ReplyTo
}
return nil
}
func (m *MailMessage_Header) GetRaw() map[string]*MailMessage_Header_HeaderValues {
if m != nil {
return m.Raw
}
return nil
}
type MailMessage_Header_MailAddress struct {
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MailMessage_Header_MailAddress) Reset() { *m = MailMessage_Header_MailAddress{} }
func (m *MailMessage_Header_MailAddress) String() string { return proto.CompactTextString(m) }
func (*MailMessage_Header_MailAddress) ProtoMessage() {}
func (*MailMessage_Header_MailAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_d142ecb16d253662, []int{0, 0, 0}
}
func (m *MailMessage_Header_MailAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MailMessage_Header_MailAddress.Unmarshal(m, b)
}
func (m *MailMessage_Header_MailAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MailMessage_Header_MailAddress.Marshal(b, m, deterministic)
}
func (m *MailMessage_Header_MailAddress) XXX_Merge(src proto.Message) {
xxx_messageInfo_MailMessage_Header_MailAddress.Merge(m, src)
}
func (m *MailMessage_Header_MailAddress) XXX_Size() int {
return xxx_messageInfo_MailMessage_Header_MailAddress.Size(m)
}
func (m *MailMessage_Header_MailAddress) XXX_DiscardUnknown() {
xxx_messageInfo_MailMessage_Header_MailAddress.DiscardUnknown(m)
}
var xxx_messageInfo_MailMessage_Header_MailAddress proto.InternalMessageInfo
func (m *MailMessage_Header_MailAddress) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func (m *MailMessage_Header_MailAddress) GetName() string {
if m != nil {
return m.Name
}
return ""
}
type MailMessage_Header_HeaderValues struct {
Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MailMessage_Header_HeaderValues) Reset() { *m = MailMessage_Header_HeaderValues{} }
func (m *MailMessage_Header_HeaderValues) String() string { return proto.CompactTextString(m) }
func (*MailMessage_Header_HeaderValues) ProtoMessage() {}
func (*MailMessage_Header_HeaderValues) Descriptor() ([]byte, []int) {
return fileDescriptor_d142ecb16d253662, []int{0, 0, 1}
}
func (m *MailMessage_Header_HeaderValues) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MailMessage_Header_HeaderValues.Unmarshal(m, b)
}
func (m *MailMessage_Header_HeaderValues) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MailMessage_Header_HeaderValues.Marshal(b, m, deterministic)
}
func (m *MailMessage_Header_HeaderValues) XXX_Merge(src proto.Message) {
xxx_messageInfo_MailMessage_Header_HeaderValues.Merge(m, src)
}
func (m *MailMessage_Header_HeaderValues) XXX_Size() int {
return xxx_messageInfo_MailMessage_Header_HeaderValues.Size(m)
}
func (m *MailMessage_Header_HeaderValues) XXX_DiscardUnknown() {
xxx_messageInfo_MailMessage_Header_HeaderValues.DiscardUnknown(m)
}
var xxx_messageInfo_MailMessage_Header_HeaderValues proto.InternalMessageInfo
func (m *MailMessage_Header_HeaderValues) GetValues() []string {
if m != nil {
return m.Values
}
return nil
}
func init() {
proto.RegisterType((*MailMessage)(nil), "system.MailMessage")
proto.RegisterType((*MailMessage_Header)(nil), "system.MailMessage.Header")
proto.RegisterMapType((map[string]*MailMessage_Header_HeaderValues)(nil), "system.MailMessage.Header.RawEntry")
proto.RegisterType((*MailMessage_Header_MailAddress)(nil), "system.MailMessage.Header.MailAddress")
proto.RegisterType((*MailMessage_Header_HeaderValues)(nil), "system.MailMessage.Header.HeaderValues")
}
func init() { proto.RegisterFile("mail_message.proto", fileDescriptor_d142ecb16d253662) }
var fileDescriptor_d142ecb16d253662 = []byte{
// 377 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcd, 0x8a, 0xdb, 0x30,
0x14, 0x85, 0xf1, 0x7f, 0x72, 0x1d, 0x48, 0xd0, 0xa2, 0x08, 0x6f, 0x6a, 0x5a, 0x48, 0xbd, 0x72,
0xc0, 0xa5, 0x25, 0xa4, 0x14, 0xda, 0x40, 0xa1, 0x14, 0xb2, 0x11, 0xa1, 0x8b, 0xd9, 0x04, 0xc5,
0x56, 0x32, 0x61, 0xec, 0xc8, 0x48, 0xca, 0x04, 0xbf, 0xe1, 0xbc, 0xc4, 0xbc, 0xcb, 0x60, 0xc9,
0x86, 0x6c, 0x26, 0xe0, 0x95, 0xcf, 0xbd, 0x9c, 0xef, 0xe8, 0xc0, 0x35, 0xa0, 0x8a, 0x9e, 0xca,
0x5d, 0xc5, 0xa4, 0xa4, 0x47, 0x96, 0xd6, 0x82, 0x2b, 0x8e, 0x7c, 0xd9, 0x48, 0xc5, 0xaa, 0xe8,
0xe3, 0x91, 0xf3, 0x63, 0xc9, 0x16, 0x7a, 0xbb, 0xbf, 0x1c, 0x16, 0xea, 0x54, 0x31, 0xa9, 0x68,
0x55, 0x1b, 0xe3, 0xa7, 0x17, 0x0f, 0xc2, 0x0d, 0x3d, 0x95, 0x1b, 0x83, 0xa3, 0x0c, 0xfc, 0x47,
0x46, 0x0b, 0x26, 0xb0, 0x1f, 0x5b, 0x49, 0x98, 0x45, 0xa9, 0x49, 0x4a, 0x6f, 0x4c, 0xe9, 0x5f,
0xed, 0x20, 0x9d, 0x13, 0x61, 0x08, 0x04, 0xbd, 0xae, 0x79, 0xd1, 0xe0, 0x69, 0x6c, 0x25, 0x13,
0xd2, 0x8f, 0xd1, 0xab, 0x0b, 0xbe, 0x31, 0xa3, 0x14, 0xdc, 0x82, 0x2a, 0x86, 0xad, 0x2e, 0xd6,
0x14, 0x4b, 0xfb, 0x62, 0xe9, 0xb6, 0x2f, 0x46, 0xb4, 0x0f, 0x7d, 0x07, 0x5b, 0x71, 0x6c, 0xc7,
0x4e, 0x12, 0x66, 0xf3, 0xf7, 0x4b, 0xe8, 0xd5, 0xef, 0xa2, 0x10, 0x4c, 0x4a, 0x62, 0x2b, 0xde,
0x72, 0x79, 0x8e, 0x9d, 0x61, 0x5c, 0x9e, 0xa3, 0x25, 0x38, 0xfb, 0x3c, 0xc7, 0xee, 0x20, 0xb0,
0x45, 0xd0, 0x0a, 0xdc, 0x83, 0xe0, 0x15, 0xf6, 0x06, 0xa1, 0x9a, 0x41, 0xbf, 0x20, 0x10, 0xac,
0x2e, 0x9b, 0x2d, 0xc7, 0xfe, 0x20, 0xbc, 0xc7, 0xd0, 0x37, 0x70, 0x04, 0xbd, 0xe2, 0xa9, 0xa6,
0x3f, 0xdf, 0xa1, 0x09, 0xbd, 0xfe, 0x39, 0x2b, 0xd1, 0x90, 0xd6, 0x1f, 0xfd, 0x30, 0x67, 0xef,
0xe2, 0xda, 0x13, 0x52, 0x23, 0xf5, 0x81, 0xc6, 0xa4, 0x1f, 0x11, 0x02, 0xf7, 0x4c, 0x2b, 0x86,
0x6d, 0xbd, 0xd6, 0x3a, 0x9a, 0xc3, 0xc4, 0x84, 0xfe, 0xa7, 0xe5, 0x85, 0x49, 0xf4, 0x01, 0xfc,
0x67, 0xad, 0xb0, 0x15, 0x3b, 0xc9, 0x98, 0x74, 0x53, 0xb4, 0x83, 0x51, 0xff, 0x2a, 0x9a, 0x81,
0xf3, 0xc4, 0x9a, 0x2e, 0xbd, 0x95, 0xe8, 0x27, 0x78, 0xda, 0xa7, 0xa3, 0xc3, 0xec, 0xcb, 0x9d,
0xee, 0xb7, 0xaf, 0x11, 0x43, 0xad, 0xec, 0xa5, 0xf5, 0xcf, 0x1d, 0x05, 0xb3, 0x29, 0xf1, 0x6a,
0x2a, 0x94, 0x5c, 0x07, 0x0f, 0x9e, 0xf9, 0x9b, 0x7c, 0xfd, 0xf9, 0xfa, 0x16, 0x00, 0x00, 0xff,
0xff, 0x16, 0x0c, 0x9f, 0x19, 0x19, 0x03, 0x00, 0x00,
}

View File

@@ -0,0 +1,180 @@
package rest
import (
"context"
"github.com/pkg/errors"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/pkg/rh"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/rest/request"
)
var _ = errors.Wrap
type (
automationScriptPayload struct {
*automation.Script
CanGrant bool `json:"canGrant"`
CanUpdate bool `json:"canUpdate"`
CanDelete bool `json:"canDelete"`
CanSetRunner bool `json:"canSetRunner"`
CanSetAsAsync bool `json:"canSetAsAsync"`
CanSetAsCritical bool `json:"canAsCritical"`
}
automationScriptSetPayload struct {
Filter automation.ScriptFilter `json:"filter"`
Set []*automationScriptPayload `json:"set"`
}
automationScriptRunnablePayload struct {
Set []*automationScriptRunnable `json:"set"`
}
automationScriptRunnable struct {
ScriptID uint64 `json:"scriptID,string"`
Name string `json:"name"`
Events map[string][]string `json:"events"`
Source string `json:"source,omitempty"`
Async bool `json:"async"`
}
AutomationScript struct {
scripts automationScriptService
runner automationScriptRunner
ac automationScriptAccessController
}
automationScriptService interface {
FindByID(context.Context, uint64) (*automation.Script, error)
Find(context.Context, automation.ScriptFilter) (automation.ScriptSet, automation.ScriptFilter, error)
Create(context.Context, *automation.Script) error
Update(context.Context, *automation.Script) error
Delete(context.Context, uint64) error
}
automationScriptRunner interface {
RecordScriptTester(context.Context, string, interface{}) error
}
automationScriptAccessController interface {
CanGrant(context.Context) bool
CanUpdateAutomationScript(context.Context, *automation.Script) bool
CanDeleteAutomationScript(context.Context, *automation.Script) bool
}
)
func (AutomationScript) New() *AutomationScript {
return &AutomationScript{
scripts: service.DefaultAutomationScriptManager,
runner: service.DefaultAutomationRunner,
ac: service.DefaultAccessControl,
}
}
func (ctrl AutomationScript) List(ctx context.Context, r *request.AutomationScriptList) (interface{}, error) {
set, filter, err := ctrl.scripts.Find(ctx, automation.ScriptFilter{
Query: r.Query,
Resource: r.Resource,
IncDeleted: false,
PageFilter: rh.Paging(r.Page, r.PerPage),
})
return ctrl.makeFilterPayload(ctx, set, filter, err)
}
func (ctrl AutomationScript) Create(ctx context.Context, r *request.AutomationScriptCreate) (interface{}, error) {
var (
script = &automation.Script{
Name: r.Name,
SourceRef: r.SourceRef,
Source: r.Source,
Async: r.Async,
RunAs: r.RunAs,
Timeout: r.Timeout,
Critical: r.Critical,
Enabled: r.Enabled,
}
)
script.AddTrigger(automation.STMS_FRESH, r.Triggers...)
return ctrl.makePayload(ctx, script, ctrl.scripts.Create(ctx, script))
}
func (ctrl AutomationScript) Read(ctx context.Context, r *request.AutomationScriptRead) (interface{}, error) {
script, err := ctrl.scripts.FindByID(ctx, r.ScriptID)
return ctrl.makePayload(ctx, script, err)
}
func (ctrl AutomationScript) Update(ctx context.Context, r *request.AutomationScriptUpdate) (interface{}, error) {
mod := &automation.Script{
ID: r.ScriptID,
Name: r.Name,
SourceRef: r.SourceRef,
Source: r.Source,
Async: r.Async,
RunAs: r.RunAs,
Timeout: r.Timeout,
Critical: r.Critical,
Enabled: r.Enabled,
}
mod.AddTrigger(automation.STMS_UPDATE, r.Triggers...)
return ctrl.makePayload(ctx, mod, ctrl.scripts.Update(ctx, mod))
}
func (ctrl AutomationScript) Delete(ctx context.Context, r *request.AutomationScriptDelete) (interface{}, error) {
return resputil.OK(), ctrl.scripts.Delete(ctx, r.ScriptID)
}
func (ctrl AutomationScript) Test(ctx context.Context, r *request.AutomationScriptTest) (interface{}, error) {
var (
err error
)
if err = ctrl.runner.RecordScriptTester(ctx, r.Source, r.Payload); err != nil {
return nil, err
}
return r.Payload, err
}
func (ctrl AutomationScript) makePayload(ctx context.Context, s *automation.Script, err error) (*automationScriptPayload, error) {
if err != nil || s == nil {
return nil, err
}
return &automationScriptPayload{
Script: s,
CanGrant: ctrl.ac.CanGrant(ctx),
CanUpdate: ctrl.ac.CanUpdateAutomationScript(ctx, s),
CanDelete: ctrl.ac.CanDeleteAutomationScript(ctx, s),
CanSetRunner: ctrl.ac.CanGrant(ctx),
CanSetAsCritical: true,
CanSetAsAsync: true,
}, nil
}
func (ctrl AutomationScript) makeFilterPayload(ctx context.Context, nn automation.ScriptSet, f automation.ScriptFilter, err error) (*automationScriptSetPayload, error) {
if err != nil {
return nil, err
}
modp := &automationScriptSetPayload{Filter: f, Set: make([]*automationScriptPayload, len(nn))}
for i := range nn {
modp.Set[i], _ = ctrl.makePayload(ctx, nn[i], nil)
}
return modp, nil
}

View File

@@ -0,0 +1,166 @@
package rest
import (
"context"
"github.com/pkg/errors"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/pkg/automation"
"github.com/cortezaproject/corteza-server/pkg/rh"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/rest/request"
)
var _ = errors.Wrap
type (
automationTriggerPayload struct {
*automation.Trigger
CanRun bool `json:"canRun"`
}
automationTriggerSetPayload struct {
Filter automation.TriggerFilter `json:"filter"`
Set []*automationTriggerPayload `json:"set"`
}
AutomationTrigger struct {
triggers automationTriggerService
scripts automationScriptFinderService
ac automationTriggerAccessController
}
automationTriggerService interface {
FindByID(context.Context, uint64) (*automation.Trigger, error)
Find(context.Context, automation.TriggerFilter) (automation.TriggerSet, automation.TriggerFilter, error)
Create(context.Context, *automation.Script, *automation.Trigger) error
Update(context.Context, *automation.Script, *automation.Trigger) error
Delete(context.Context, *automation.Script, *automation.Trigger) error
}
automationScriptFinderService interface {
FindByID(context.Context, uint64) (*automation.Script, error)
}
automationTriggerAccessController interface {
CanGrant(context.Context) bool
CanRunAutomationTrigger(context.Context, *automation.Trigger) bool
}
)
func (AutomationTrigger) New() *AutomationTrigger {
return &AutomationTrigger{
scripts: service.DefaultAutomationScriptManager,
triggers: service.DefaultAutomationTriggerManager,
ac: service.DefaultAccessControl,
}
}
func (ctrl AutomationTrigger) List(ctx context.Context, r *request.AutomationTriggerList) (interface{}, error) {
set, filter, err := ctrl.triggers.Find(ctx, automation.TriggerFilter{
Resource: r.Resource,
Event: r.Event,
ScriptID: r.ScriptID,
IncDeleted: false,
PageFilter: rh.Paging(r.Page, r.PerPage),
})
return ctrl.makeFilterPayload(ctx, set, filter, err)
}
func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationTriggerCreate) (interface{}, error) {
s, _, err := ctrl.loadCombo(ctx, r.ScriptID, 0)
if err != nil {
return nil, errors.Wrap(err, "can not create trigger")
}
var (
t = &automation.Trigger{
Event: r.Event,
Resource: r.Resource,
Condition: r.Condition,
ScriptID: s.ID,
Enabled: r.Enabled,
}
)
return ctrl.makePayload(ctx, t, ctrl.triggers.Create(ctx, s, t))
}
func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTriggerRead) (interface{}, error) {
_, t, err := ctrl.loadCombo(ctx, r.ScriptID, 0)
if err != nil {
return nil, errors.Wrap(err, "can not read trigger")
}
return ctrl.makePayload(ctx, t, err)
}
func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationTriggerUpdate) (interface{}, error) {
s, t, err := ctrl.loadCombo(ctx, r.ScriptID, r.TriggerID)
if err != nil {
return nil, errors.Wrap(err, "can not update trigger")
}
t.Event = r.Event
t.Resource = r.Resource
t.Condition = r.Condition
t.ScriptID = r.ScriptID
t.Enabled = r.Enabled
return ctrl.makePayload(ctx, t, ctrl.triggers.Update(ctx, s, t))
}
func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationTriggerDelete) (interface{}, error) {
s, t, err := ctrl.loadCombo(ctx, r.ScriptID, r.TriggerID)
if err != nil {
return nil, errors.Wrap(err, "can not update trigger")
}
return resputil.OK(), ctrl.triggers.Delete(ctx, s, t)
}
func (ctrl AutomationTrigger) loadCombo(ctx context.Context, scriptID, triggerID uint64) (s *automation.Script, t *automation.Trigger, err error) {
if triggerID > 0 {
t, err = ctrl.triggers.FindByID(ctx, triggerID)
return
}
if scriptID > 0 {
s, err = ctrl.scripts.FindByID(ctx, scriptID)
return
}
return
}
func (ctrl AutomationTrigger) makePayload(ctx context.Context, t *automation.Trigger, err error) (*automationTriggerPayload, error) {
if err != nil || t == nil {
return nil, err
}
return &automationTriggerPayload{
Trigger: t,
CanRun: ctrl.ac.CanRunAutomationTrigger(ctx, t),
}, nil
}
func (ctrl AutomationTrigger) makeFilterPayload(ctx context.Context, nn automation.TriggerSet, f automation.TriggerFilter, err error) (*automationTriggerSetPayload, error) {
if err != nil {
return nil, err
}
modp := &automationTriggerSetPayload{Filter: f, Set: make([]*automationTriggerPayload, len(nn))}
for i := range nn {
modp.Set[i], _ = ctrl.makePayload(ctx, nn[i], nil)
}
return modp, nil
}

View File

@@ -0,0 +1,185 @@
package handlers
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `automation_script.go`, `automation_script.util.go` or `automation_script_test.go` to
implement your API calls, helper functions and tests. The file `automation_script.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"context"
"net/http"
"github.com/go-chi/chi"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/system/rest/request"
)
// Internal API interface
type AutomationScriptAPI interface {
List(context.Context, *request.AutomationScriptList) (interface{}, error)
Create(context.Context, *request.AutomationScriptCreate) (interface{}, error)
Read(context.Context, *request.AutomationScriptRead) (interface{}, error)
Update(context.Context, *request.AutomationScriptUpdate) (interface{}, error)
Delete(context.Context, *request.AutomationScriptDelete) (interface{}, error)
Test(context.Context, *request.AutomationScriptTest) (interface{}, error)
}
// HTTP API interface
type AutomationScript struct {
List func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
Test func(http.ResponseWriter, *http.Request)
}
func NewAutomationScript(h AutomationScriptAPI) *AutomationScript {
return &AutomationScript{
List: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptList()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.List", r, err)
resputil.JSON(w, err)
return
}
value, err := h.List(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.List", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.List", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Create: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptCreate()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.Create", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Create(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.Create", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.Create", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Read: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptRead()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.Read", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Read(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.Read", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.Read", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Update: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptUpdate()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.Update", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Update(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.Update", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.Update", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Delete: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptDelete()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.Delete", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Delete(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.Delete", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.Delete", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Test: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationScriptTest()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationScript.Test", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Test(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationScript.Test", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationScript.Test", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
}
}
func (h AutomationScript) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Get("/automation/script/", h.List)
r.Post("/automation/script/", h.Create)
r.Get("/automation/script/{scriptID}", h.Read)
r.Post("/automation/script/{scriptID}", h.Update)
r.Delete("/automation/script/{scriptID}", h.Delete)
r.Post("/automation/script/test", h.Test)
})
}

View File

@@ -0,0 +1,162 @@
package handlers
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `automation_trigger.go`, `automation_trigger.util.go` or `automation_trigger_test.go` to
implement your API calls, helper functions and tests. The file `automation_trigger.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"context"
"net/http"
"github.com/go-chi/chi"
"github.com/titpetric/factory/resputil"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/system/rest/request"
)
// Internal API interface
type AutomationTriggerAPI interface {
List(context.Context, *request.AutomationTriggerList) (interface{}, error)
Create(context.Context, *request.AutomationTriggerCreate) (interface{}, error)
Read(context.Context, *request.AutomationTriggerRead) (interface{}, error)
Update(context.Context, *request.AutomationTriggerUpdate) (interface{}, error)
Delete(context.Context, *request.AutomationTriggerDelete) (interface{}, error)
}
// HTTP API interface
type AutomationTrigger struct {
List func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
}
func NewAutomationTrigger(h AutomationTriggerAPI) *AutomationTrigger {
return &AutomationTrigger{
List: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationTriggerList()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationTrigger.List", r, err)
resputil.JSON(w, err)
return
}
value, err := h.List(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationTrigger.List", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationTrigger.List", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Create: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationTriggerCreate()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationTrigger.Create", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Create(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationTrigger.Create", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationTrigger.Create", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Read: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationTriggerRead()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationTrigger.Read", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Read(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationTrigger.Read", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationTrigger.Read", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Update: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationTriggerUpdate()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationTrigger.Update", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Update(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationTrigger.Update", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationTrigger.Update", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Delete: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewAutomationTriggerDelete()
if err := params.Fill(r); err != nil {
logger.LogParamError("AutomationTrigger.Delete", r, err)
resputil.JSON(w, err)
return
}
value, err := h.Delete(r.Context(), params)
if err != nil {
logger.LogControllerError("AutomationTrigger.Delete", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("AutomationTrigger.Delete", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
}
}
func (h AutomationTrigger) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) {
r.Group(func(r chi.Router) {
r.Use(middlewares...)
r.Get("/automation/script/{scriptID}/trigger/", h.List)
r.Post("/automation/script/{scriptID}/trigger/", h.Create)
r.Get("/automation/script/{scriptID}/trigger/{triggerID}", h.Read)
r.Post("/automation/script/{scriptID}/trigger/{triggerID}", h.Update)
r.Delete("/automation/script/{scriptID}/trigger/{triggerID}", h.Delete)
})
}

View File

@@ -0,0 +1,449 @@
package request
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `automation_script.go`, `automation_script.util.go` or `automation_script_test.go` to
implement your API calls, helper functions and tests. The file `automation_script.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"io"
"strings"
"encoding/json"
"mime/multipart"
"net/http"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"github.com/cortezaproject/corteza-server/pkg/automation"
)
var _ = chi.URLParam
var _ = multipart.FileHeader{}
// AutomationScript list request parameters
type AutomationScriptList struct {
Query string
Resource string
IncDeleted bool
Page uint
PerPage uint
}
func NewAutomationScriptList() *AutomationScriptList {
return &AutomationScriptList{}
}
func (r AutomationScriptList) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["query"] = r.Query
out["resource"] = r.Resource
out["incDeleted"] = r.IncDeleted
out["page"] = r.Page
out["perPage"] = r.PerPage
return out
}
func (r *AutomationScriptList) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := get["query"]; ok {
r.Query = val
}
if val, ok := get["resource"]; ok {
r.Resource = val
}
if val, ok := get["incDeleted"]; ok {
r.IncDeleted = parseBool(val)
}
if val, ok := get["page"]; ok {
r.Page = parseUint(val)
}
if val, ok := get["perPage"]; ok {
r.PerPage = parseUint(val)
}
return err
}
var _ RequestFiller = NewAutomationScriptList()
// AutomationScript create request parameters
type AutomationScriptCreate struct {
Name string
SourceRef string
Source string
RunAs uint64 `json:",string"`
Timeout uint
Critical bool
Async bool
Enabled bool
Triggers automation.TriggerSet
}
func NewAutomationScriptCreate() *AutomationScriptCreate {
return &AutomationScriptCreate{}
}
func (r AutomationScriptCreate) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["name"] = r.Name
out["sourceRef"] = r.SourceRef
out["source"] = r.Source
out["runAs"] = r.RunAs
out["timeout"] = r.Timeout
out["critical"] = r.Critical
out["async"] = r.Async
out["enabled"] = r.Enabled
out["triggers"] = r.Triggers
return out
}
func (r *AutomationScriptCreate) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := post["name"]; ok {
r.Name = val
}
if val, ok := post["sourceRef"]; ok {
r.SourceRef = val
}
if val, ok := post["source"]; ok {
r.Source = val
}
if val, ok := post["runAs"]; ok {
r.RunAs = parseUInt64(val)
}
if val, ok := post["timeout"]; ok {
r.Timeout = parseUint(val)
}
if val, ok := post["critical"]; ok {
r.Critical = parseBool(val)
}
if val, ok := post["async"]; ok {
r.Async = parseBool(val)
}
if val, ok := post["enabled"]; ok {
r.Enabled = parseBool(val)
}
return err
}
var _ RequestFiller = NewAutomationScriptCreate()
// AutomationScript read request parameters
type AutomationScriptRead struct {
ScriptID uint64 `json:",string"`
}
func NewAutomationScriptRead() *AutomationScriptRead {
return &AutomationScriptRead{}
}
func (r AutomationScriptRead) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationScriptRead) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationScriptRead()
// AutomationScript update request parameters
type AutomationScriptUpdate struct {
ScriptID uint64 `json:",string"`
Name string
SourceRef string
Source string
RunAs uint64 `json:",string"`
Timeout uint
Critical bool
Async bool
Enabled bool
Triggers automation.TriggerSet
}
func NewAutomationScriptUpdate() *AutomationScriptUpdate {
return &AutomationScriptUpdate{}
}
func (r AutomationScriptUpdate) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["scriptID"] = r.ScriptID
out["name"] = r.Name
out["sourceRef"] = r.SourceRef
out["source"] = r.Source
out["runAs"] = r.RunAs
out["timeout"] = r.Timeout
out["critical"] = r.Critical
out["async"] = r.Async
out["enabled"] = r.Enabled
out["triggers"] = r.Triggers
return out
}
func (r *AutomationScriptUpdate) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
if val, ok := post["name"]; ok {
r.Name = val
}
if val, ok := post["sourceRef"]; ok {
r.SourceRef = val
}
if val, ok := post["source"]; ok {
r.Source = val
}
if val, ok := post["runAs"]; ok {
r.RunAs = parseUInt64(val)
}
if val, ok := post["timeout"]; ok {
r.Timeout = parseUint(val)
}
if val, ok := post["critical"]; ok {
r.Critical = parseBool(val)
}
if val, ok := post["async"]; ok {
r.Async = parseBool(val)
}
if val, ok := post["enabled"]; ok {
r.Enabled = parseBool(val)
}
return err
}
var _ RequestFiller = NewAutomationScriptUpdate()
// AutomationScript delete request parameters
type AutomationScriptDelete struct {
ScriptID uint64 `json:",string"`
}
func NewAutomationScriptDelete() *AutomationScriptDelete {
return &AutomationScriptDelete{}
}
func (r AutomationScriptDelete) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationScriptDelete) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationScriptDelete()
// AutomationScript test request parameters
type AutomationScriptTest struct {
Source string
Payload json.RawMessage
}
func NewAutomationScriptTest() *AutomationScriptTest {
return &AutomationScriptTest{}
}
func (r AutomationScriptTest) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["source"] = r.Source
out["payload"] = r.Payload
return out
}
func (r *AutomationScriptTest) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := post["source"]; ok {
r.Source = val
}
if val, ok := post["payload"]; ok {
r.Payload = json.RawMessage(val)
}
return err
}
var _ RequestFiller = NewAutomationScriptTest()

View File

@@ -0,0 +1,360 @@
package request
/*
Hello! This file is auto-generated from `docs/src/spec.json`.
For development:
In order to update the generated files, edit this file under the location,
add your struct fields, imports, API definitions and whatever you want, and:
1. run [spec](https://github.com/titpetric/spec) in the same folder,
2. run `./_gen.php` in this folder.
You may edit `automation_trigger.go`, `automation_trigger.util.go` or `automation_trigger_test.go` to
implement your API calls, helper functions and tests. The file `automation_trigger.go`
is only generated the first time, and will not be overwritten if it exists.
*/
import (
"io"
"strings"
"encoding/json"
"mime/multipart"
"net/http"
"github.com/go-chi/chi"
"github.com/pkg/errors"
)
var _ = chi.URLParam
var _ = multipart.FileHeader{}
// AutomationTrigger list request parameters
type AutomationTriggerList struct {
Resource string
Event string
IncDeleted bool
Page uint
PerPage uint
ScriptID uint64 `json:",string"`
}
func NewAutomationTriggerList() *AutomationTriggerList {
return &AutomationTriggerList{}
}
func (r AutomationTriggerList) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["resource"] = r.Resource
out["event"] = r.Event
out["incDeleted"] = r.IncDeleted
out["page"] = r.Page
out["perPage"] = r.PerPage
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationTriggerList) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := get["resource"]; ok {
r.Resource = val
}
if val, ok := get["event"]; ok {
r.Event = val
}
if val, ok := get["incDeleted"]; ok {
r.IncDeleted = parseBool(val)
}
if val, ok := get["page"]; ok {
r.Page = parseUint(val)
}
if val, ok := get["perPage"]; ok {
r.PerPage = parseUint(val)
}
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationTriggerList()
// AutomationTrigger create request parameters
type AutomationTriggerCreate struct {
Resource string
Event string
Condition string
Enabled bool
ScriptID uint64 `json:",string"`
}
func NewAutomationTriggerCreate() *AutomationTriggerCreate {
return &AutomationTriggerCreate{}
}
func (r AutomationTriggerCreate) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["resource"] = r.Resource
out["event"] = r.Event
out["condition"] = r.Condition
out["enabled"] = r.Enabled
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationTriggerCreate) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if val, ok := post["resource"]; ok {
r.Resource = val
}
if val, ok := post["event"]; ok {
r.Event = val
}
if val, ok := post["condition"]; ok {
r.Condition = val
}
if val, ok := post["enabled"]; ok {
r.Enabled = parseBool(val)
}
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationTriggerCreate()
// AutomationTrigger read request parameters
type AutomationTriggerRead struct {
TriggerID uint64 `json:",string"`
ScriptID uint64 `json:",string"`
}
func NewAutomationTriggerRead() *AutomationTriggerRead {
return &AutomationTriggerRead{}
}
func (r AutomationTriggerRead) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["triggerID"] = r.TriggerID
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationTriggerRead) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.TriggerID = parseUInt64(chi.URLParam(req, "triggerID"))
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationTriggerRead()
// AutomationTrigger update request parameters
type AutomationTriggerUpdate struct {
TriggerID uint64 `json:",string"`
ScriptID uint64 `json:",string"`
Resource string
Event string
Condition string
Enabled bool
}
func NewAutomationTriggerUpdate() *AutomationTriggerUpdate {
return &AutomationTriggerUpdate{}
}
func (r AutomationTriggerUpdate) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["triggerID"] = r.TriggerID
out["scriptID"] = r.ScriptID
out["resource"] = r.Resource
out["event"] = r.Event
out["condition"] = r.Condition
out["enabled"] = r.Enabled
return out
}
func (r *AutomationTriggerUpdate) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.TriggerID = parseUInt64(chi.URLParam(req, "triggerID"))
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
if val, ok := post["resource"]; ok {
r.Resource = val
}
if val, ok := post["event"]; ok {
r.Event = val
}
if val, ok := post["condition"]; ok {
r.Condition = val
}
if val, ok := post["enabled"]; ok {
r.Enabled = parseBool(val)
}
return err
}
var _ RequestFiller = NewAutomationTriggerUpdate()
// AutomationTrigger delete request parameters
type AutomationTriggerDelete struct {
TriggerID uint64 `json:",string"`
ScriptID uint64 `json:",string"`
}
func NewAutomationTriggerDelete() *AutomationTriggerDelete {
return &AutomationTriggerDelete{}
}
func (r AutomationTriggerDelete) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["triggerID"] = r.TriggerID
out["scriptID"] = r.ScriptID
return out
}
func (r *AutomationTriggerDelete) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.TriggerID = parseUInt64(chi.URLParam(req, "triggerID"))
r.ScriptID = parseUInt64(chi.URLParam(req, "scriptID"))
return err
}
var _ RequestFiller = NewAutomationTriggerDelete()

View File

@@ -4,6 +4,7 @@ import (
"github.com/go-chi/chi"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/system/internal/service"
"github.com/cortezaproject/corteza-server/system/rest/handlers"
)
@@ -13,6 +14,13 @@ func MountRoutes(r chi.Router) {
r.Group(func(r chi.Router) {
handlers.NewAuth((Auth{}).New()).MountRoutes(r)
handlers.NewAuthInternal((AuthInternal{}).New()).MountRoutes(r)
// A special case that, we do not add this through standard request, handlers & controllers
// combo but directly -- we need access to r.Body
r.Handle("/sink", &Sink{
svc: service.DefaultSink,
sign: auth.DefaultSigner,
})
})
// Protect all _private_ routes
@@ -25,5 +33,8 @@ func MountRoutes(r chi.Router) {
handlers.NewPermissions(Permissions{}.New()).MountRoutes(r)
handlers.NewApplication(Application{}.New()).MountRoutes(r)
handlers.NewSettings(Settings{}.New()).MountRoutes(r)
handlers.NewAutomationScript(AutomationScript{}.New()).MountRoutes(r)
handlers.NewAutomationTrigger(AutomationTrigger{}.New()).MountRoutes(r)
})
}

89
system/rest/sink.go Normal file
View File

@@ -0,0 +1,89 @@
package rest
import (
"context"
"io"
"net/http"
"strings"
"time"
"github.com/pkg/errors"
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/system/internal/service"
)
var _ = errors.Wrap
type Sink struct {
// xxx service.XXXService
svc interface {
Process(context.Context, string, io.Reader) error
}
sign auth.Signer
}
func (ctrl *Sink) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
// What are we getting + part of the signature
contentType = r.URL.Query().Get("content-type")
//
sign = r.URL.Query().Get("sign")
origin = r.URL.Query().Get("origin")
expires = r.URL.Query().Get("expires")
method = strings.ToUpper(r.Method)
unsupported = func() {
http.Error(w, "unsupported content-type", http.StatusBadRequest)
}
)
if sign == "" {
http.Error(w, "signature missing", http.StatusUnauthorized)
return
}
if ctrl.sign.Verify(sign, 0, method, "/sink", contentType, origin, expires) {
http.Error(w, "invalid signature", http.StatusForbidden)
return
}
if expires != "" {
if exp, err := time.Parse("2006-01-02", expires); err != nil {
http.Error(w, "could not process expiration date", http.StatusInternalServerError)
return
} else if exp.Before(time.Now()) {
http.Error(w, "signature expired", http.StatusGone)
return
}
}
if contentType == "" {
// If content-type not explicitly set (via QS),
// try to get it from the headers
contentType = r.Header.Get("content-type")
if i := strings.Index(contentType, ";"); i > 0 {
// intentionally > 0
contentType = contentType[0 : i-1]
}
}
if contentType == "" {
unsupported()
return
}
defer r.Body.Close()
switch ctrl.svc.Process(ctx, contentType, r.Body) {
case service.ErrSinkContentProcessingFailed:
http.Error(w, "sink processing failed", http.StatusInternalServerError)
case service.ErrSinkContentTypeUnsupported:
unsupported()
}
}

View File

@@ -43,7 +43,9 @@ func Configure() *cli.Config {
servicesInitialized = true
// storagePath := options.EnvString("", "SYSTEM_STORAGE_PATH", "var/store")
cli.HandleError(service.Init(ctx, c.Log))
cli.HandleError(service.Init(ctx, c.Log, service.Config{
Corredor: *c.ScriptRunner,
}))
},
@@ -127,6 +129,9 @@ func Configure() *cli.Config {
func(ctx context.Context, c *cli.Config) *cobra.Command {
return commands.Roles(ctx, c)
},
func(ctx context.Context, c *cli.Config) *cobra.Command {
return commands.Sink(ctx, c)
},
},
ProvisionMigrateDatabase: cli.Runners{

View File

@@ -0,0 +1,32 @@
package types
import (
"net/mail"
"time"
)
type (
MailMessage struct {
Date time.Time `json:"date"`
Header MailMessageHeader `json:"header"`
// RawBody will be base64 encoded!
// (might contain binary data)
RawBody []byte `json:"rawBody,string"`
// @todo parts
// Parts []...
}
MailMessageHeader struct {
// extract common addresses
To []*mail.Address `json:"to"`
CC []*mail.Address `json:"cc"`
BCC []*mail.Address `json:"bcc"`
From []*mail.Address `json:"from"`
ReplyTo []*mail.Address `json:"replyTo"`
Raw mail.Header `json:"raw"`
}
)

View File

@@ -9,3 +9,5 @@ const ApplicationPermissionResource = permissions.Resource("system:application:"
const OrganisationPermissionResource = permissions.Resource("system:organisation:")
const UserPermissionResource = permissions.Resource("system:user:")
const RolePermissionResource = permissions.Resource("system:role:")
const AutomationScriptPermissionResource = permissions.Resource("system:automation-script:")
const AutomationTriggerPermissionResource = permissions.Resource("system:automation-trigger:")