Merge branch 'mail-automation'
This commit is contained in:
3
Makefile
3
Makefile
@@ -151,9 +151,6 @@ mocks: $(GOMOCK)
|
||||
########################################################################################################################
|
||||
# Toolset
|
||||
|
||||
$(GOTEST):
|
||||
$(GOGET) github.com/rakyll/gotest
|
||||
|
||||
$(REALIZE):
|
||||
$(GOGET) github.com/tockins/realize
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
226
api/system/spec/automation_script.json
Normal file
226
api/system/spec/automation_script.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
165
api/system/spec/automation_trigger.json
Normal file
165
api/system/spec/automation_trigger.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
13
codegen.sh
13
codegen.sh
@@ -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
@@ -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',
|
||||
|
||||
@@ -32,7 +32,7 @@ type (
|
||||
var (
|
||||
DefaultLogger *zap.Logger
|
||||
|
||||
// DefaultPermissions Retrives & stores permissions
|
||||
// DefaultPermissions Retrieves & stores permissions
|
||||
DefaultPermissions permissionServicer
|
||||
|
||||
// DefaultAccessControl Access control checking
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
196
pkg/automation/mail/condition.go
Normal file
196
pkg/automation/mail/condition.go
Normal 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
|
||||
}
|
||||
}
|
||||
144
pkg/automation/mail/condition_test.go
Normal file
144
pkg/automation/mail/condition_test.go
Normal 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
89
system/commands/sink.go
Normal 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
48
system/db/schema/mysql/20190902080000.automation.up.sql
Normal file
48
system/db/schema/mysql/20190902080000.automation.up.sql
Normal 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;
|
||||
@@ -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
|
||||
}
|
||||
|
||||
193
system/internal/service/automation_runner.go
Normal file
193
system/internal/service/automation_runner.go
Normal 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))
|
||||
}
|
||||
158
system/internal/service/automation_script.go
Normal file
158
system/internal/service/automation_script.go
Normal 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
|
||||
}
|
||||
120
system/internal/service/automation_trigger.go
Normal file
120
system/internal/service/automation_trigger.go
Normal 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
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
100
system/internal/service/mailproc.go
Normal file
100
system/internal/service/mailproc.go
Normal 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
|
||||
}
|
||||
61
system/internal/service/mailproc_test.go
Normal file
61
system/internal/service/mailproc_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
53
system/internal/service/sink.go
Normal file
53
system/internal/service/sink.go
Normal 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
|
||||
}
|
||||
53
system/proto/mail_message.go
Normal file
53
system/proto/mail_message.go
Normal 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: ×tamp.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
|
||||
}
|
||||
286
system/proto/mail_message.pb.go
Normal file
286
system/proto/mail_message.pb.go
Normal 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,
|
||||
}
|
||||
180
system/rest/automation_script.go
Normal file
180
system/rest/automation_script.go
Normal 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
|
||||
}
|
||||
166
system/rest/automation_trigger.go
Normal file
166
system/rest/automation_trigger.go
Normal 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
|
||||
}
|
||||
185
system/rest/handlers/automation_script.go
Normal file
185
system/rest/handlers/automation_script.go
Normal 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)
|
||||
})
|
||||
}
|
||||
162
system/rest/handlers/automation_trigger.go
Normal file
162
system/rest/handlers/automation_trigger.go
Normal 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)
|
||||
})
|
||||
}
|
||||
449
system/rest/request/automation_script.go
Normal file
449
system/rest/request/automation_script.go
Normal 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()
|
||||
360
system/rest/request/automation_trigger.go
Normal file
360
system/rest/request/automation_trigger.go
Normal 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()
|
||||
@@ -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
89
system/rest/sink.go
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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{
|
||||
|
||||
32
system/types/mail_message.go
Normal file
32
system/types/mail_message.go
Normal 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"`
|
||||
}
|
||||
)
|
||||
@@ -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:")
|
||||
|
||||
Reference in New Issue
Block a user