Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion manifest.master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,12 @@ callbacks:
type: objecttype
objecttypes:
- write_event
- name: "write (insert/update) an object over the api"
callback: write_db
filter:
type: objecttype
objecttypes:
- write_db
- name: "bounce json back for 'bounce'"
callback: bounce
filter:
Expand Down Expand Up @@ -453,7 +459,21 @@ callbacks:
type: body
args:
- type: value
value: "%_exec.pluginDir%/server/db_pre_save+write_event/write_event.py"
value: "%_exec.pluginDir%/server/db_pre_save+write_api/write_event.py"
write_db:
exec:
service: python3
commands:
- prog: python3
stdin:
type: body
stderr:
type: body
stdout:
type: body
args:
- type: value
value: "%_exec.pluginDir%/server/db_pre_save+write_api/write_db.py"
check:
exec:
service: "node"
Expand Down
130 changes: 130 additions & 0 deletions server/db_pre_save+write_api/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# encoding: utf-8


import json
import sys
import requests


# plugin response functions

# the direct response from the plugin uses stdin and stderr


def stdout(msg: str):
sys.stdout.write(msg)
sys.stdout.write('\n')


def stderr(msg: str):
sys.stderr.write(msg)
sys.stderr.write('\n')


def return_response(response: dict):
stdout(json.dumps(response))
exit(0)


def return_error_response(error: str):
stderr(f'error in fylr-plugin-example: {error}')
return_response(
{
'error': {
'code': 'fylr-plugin-example.error',
'statuscode': 400,
'description': error,
'error': error,
},
},
)


# -----------------------------

# helper functions to parse callback data from fylr

# the api url and access token are necessary if the plugin
# needs to read/write more data over the fylr api


def get_api_url(callback_data):
url = get_json_value(callback_data, 'info.api_url')
if not url:
return_error_response('info.api_url missing!')
return f'{url}/api/v1'


def get_access_token(callback_data):
access_token = get_json_value(callback_data, 'info.api_user_access_token')
if not access_token:
return_error_response('info.api_user_access_token missing!')
return access_token


# -----------------------------

# fylr api functions

# the plugin can call the fylr api to read/write more data


def fylr_api_headers(access_token):
return {'authorization': f'Bearer {access_token}'}


def get_from_api(api_url, path, access_token):
resp = requests.get(
url=f'{api_url}/{path}',
headers=fylr_api_headers(access_token),
)

return resp.text, resp.status_code


def post_to_api(api_url, path, access_token, payload=None):
resp = requests.post(
url=f'{api_url}/{path}',
headers=fylr_api_headers(access_token),
data=payload,
)

return resp.text, resp.status_code


# -----------------------------


def get_json_value(js, path, expected=False, split_char='.'):

current = js
path_parts = []
current_part = ''

for i in range(len(path)):
if path[i] != split_char:
current_part += path[i]
if i == len(path) - 1:
path_parts.append(current_part)
continue

if i > 0 and path[i - 1] == '\\':
current_part += path[i]
continue

if len(current_part) > 0:
path_parts.append(current_part)
current_part = ''

for path_part in path_parts:
path_part = path_part.replace('\\' + split_char, split_char)

if not isinstance(current, dict) or path_part not in current:
if expected:
raise Exception('expected: ' + path_part)
else:
return None

current = current[path_part]

return current
88 changes: 88 additions & 0 deletions server/db_pre_save+write_api/write_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# encoding: utf-8


import sys
import json
import util


def main():

# read the callback data from fylr
callback_data = json.loads(sys.stdin.read())

# get the server api url and access token
api_url = util.get_api_url(callback_data)

# get the oauth2 access token for the api
access_token = util.get_access_token(callback_data)

objects = util.get_json_value(callback_data, 'objects')
if not isinstance(objects, list):
util.return_response(callback_data)

updated_objects = []

# iterate over the objects and count how many were inserted/updated
for obj in objects:

if not isinstance(obj, dict):
continue

# get the "titel" from the object,
# if it is set create a new linked object with the same name.
# insert the object over the api and link it in this object
objecttype = util.get_json_value(obj, '_objecttype')
title = util.get_json_value(obj, f'{objecttype}.titel')
if not title:
continue

# create a new "linked_object" payload
# and post it to the api/v1/db endpoint
resp_text, statuscode = util.post_to_api(
api_url=api_url,
path='db/linked_object',
access_token=access_token,
payload=json.dumps(
[
{
'_comment': '<inserted by fylr-plugin-example>',
'_mask': "_all_fields",
'_objecttype': "linked_object",
"linked_object": {
"_version": 1,
"name": title,
},
}
],
indent=4,
),
)
if statuscode != 200:
# could not insert the new linked object -> api error
util.return_error_response(
f'could not insert linked_object: api error (code {statuscode}): {resp_text}'
)

# use a lookup to insert the new linked object
obj[objecttype]['link'] = {
'_mask': "_all_fields",
'_objecttype': "linked_object",
"linked_object": {
"_version": 1,
"lookup:_id": {
"name": title,
},
},
}

# this object was updated, it must be returned to fylr
updated_objects.append(obj)

# only return the objects which were updated.
# fylr will save all other objects from the callback without any changes
util.return_response({"objects": updated_objects})


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,18 @@

def main():

orig_data = json.loads(sys.stdin.read())
# read the callback data from fylr
callback_data = json.loads(sys.stdin.read())

# get the server url
api_url = util.get_json_value(orig_data, 'info.api_url')
if api_url is None:
util.return_error_response('info.api_url missing!')
api_url += '/api/v1'
# get the server api url and access token
api_url = util.get_api_url(callback_data)

# get a session token
access_token = util.get_json_value(
orig_data, 'info.api_user_access_token')
if access_token is None:
util.return_error_response('info.api_user_access_token missing!')
# get the oauth2 access token for the api
access_token = util.get_access_token(callback_data)

objects = util.get_json_value(orig_data, 'objects')
objects = util.get_json_value(callback_data, 'objects')
if not isinstance(objects, list):
util.return_response(orig_data)
util.return_response(callback_data)

objecttype_count = {}

Expand All @@ -42,7 +37,7 @@ def main():
'updated': 0,
}

version = util.get_json_value(obj, objecttype + '._version')
version = util.get_json_value(obj, f'{objecttype}._version')
if version == 1:
objecttype_count[objecttype]['inserted'] += 1
else:
Expand All @@ -54,15 +49,19 @@ def main():
api_url=api_url,
path='event?background=1',
access_token=access_token,
payload=util.dumpjs({
'event': {
'type': 'EXAMPLE_PLUGIN_OBJECT_STATISTICS',
'objecttype': objecttype,
'info': objecttype_count[objecttype]
}
})
payload=json.dumps(
{
'event': {
'type': 'EXAMPLE_PLUGIN_OBJECT_STATISTICS',
'objecttype': objecttype,
'info': objecttype_count[objecttype],
}
},
indent=4,
),
)

# return an empty objects array, this indicates to fylr that there were no changes in the objects
util.return_response({"objects": []})


Expand Down
Loading