Skip to content

Commit

Permalink
Store data from response for reuse in integrations and content (#356)
Browse files Browse the repository at this point in the history
  • Loading branch information
GDay authored Sep 26, 2023
1 parent d436721 commit 7decda7
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 1 deletion.
22 changes: 22 additions & 0 deletions back/admin/integrations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
)
from twilio.rest import Client

from admin.integrations.utils import get_value_from_notation
from misc.fernet_fields import EncryptedTextField
from misc.fields import EncryptedJSONField
from organization.models import Notification
Expand Down Expand Up @@ -288,6 +289,27 @@ def execute(self, new_hire, params):
pass
return False, response

# store data coming back from response to the user, so we can reuse in other
# integrations
if store_data := item.get("store_data", {}):
for new_hire_prop, notation_for_response in store_data.items():
try:
value = get_value_from_notation(
notation_for_response, response.json()
)
except KeyError:
return (
False,
f"Could not store data to new hire: {notation_for_response}"
f" not found in {self.clean_response(response.json())}",
)

# save to new hire and to temp var `params` on this model for use in
# the same integration
new_hire.extra_fields[new_hire_prop] = value
self.params[new_hire_prop] = value
new_hire.save()

# Run all post requests (notifications)
for item in self.manifest.get("post_execute_notification", []):
if item["type"] == "email":
Expand Down
1 change: 1 addition & 0 deletions back/admin/integrations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class ManifestExecuteSerializer(ValidateMixin, serializers.Serializer):
url = serializers.CharField()
data = serializers.JSONField(required=False, default=dict)
headers = serializers.JSONField(required=False, default=dict)
store_data = serializers.DictField(child=serializers.CharField(), default=dict)
method = serializers.ChoiceField(
[
("HEAD", "HEAD"),
Expand Down
64 changes: 64 additions & 0 deletions back/admin/integrations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,3 +491,67 @@ def test_slack_connect(client, django_user_model):

assert Integration.objects.filter(integration=Integration.Type.SLACK_BOT).exists()
assert "Slack has successfully been connected." in response.content.decode()


@pytest.mark.django_db
@patch(
"admin.integrations.models.Integration.run_request",
Mock(return_value=(True, Mock(json=lambda: {"user_data": {"user_id": 123}}))),
)
def test_integration_save_data_to_user(new_hire_factory, custom_integration_factory):
new_hire = new_hire_factory()

assert new_hire.extra_fields == {}

integration = custom_integration_factory(
manifest={
"execute": [
{
"url": "http://localhost/",
"store_data": {"FORM_ID": "user_data.user_id"},
}
]
}
)

integration.execute(new_hire, {})

new_hire.refresh_from_db()

assert new_hire.extra_fields == {"FORM_ID": 123}


@pytest.mark.django_db
@patch(
"admin.integrations.models.Integration.run_request",
Mock(return_value=(True, Mock(json=lambda: {"user": {"user_id": 123}}))),
)
def test_integration_save_data_to_user_invalid_lookup(
new_hire_factory, custom_integration_factory
):
new_hire = new_hire_factory()

assert new_hire.extra_fields == {}

integration = custom_integration_factory(
manifest={
"execute": [
{
"url": "http://localhost/",
"store_data": {"FORM_ID": "user_data.user_id"},
}
]
}
)

result = integration.execute(new_hire, {})

assert result == (
False,
"Could not store data to new hire: "
"user_data.user_id not found in {'user': {'user_id': 123}}",
)

new_hire.refresh_from_db()

assert new_hire.extra_fields == {}
12 changes: 11 additions & 1 deletion docs/Integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,17 @@ These requests will be ran when this integration gets triggered.

`method`: The request method. E.g. `POST` or `GET`.

`headers`: (optionally) This will overwrite the default headers.
`headers`: (optional) This will overwrite the default headers.

`store_data`: (optional) This can be used to store data to the new hire. Let's say you create a document through an API and you need to store the document ID that is relevant to the new hire, then you can do that with this. You can put a dictionary here with a key and value of the new hire prop name and the notation of where to get the data. You can use a dot notation to go deeper in the json. This data is only available in items assigned to this new hire and can be used anywhere (in both content items (todo, resources etc) and integrations that get triggered later).

Example:
```
{
"FORM_ID": "data.id",
"DOCUMENT_HASH": "document_hash"
}
```

### Headers
These headers will be send with every request. These could include some sort of token variable for authentication.
Expand Down

0 comments on commit 7decda7

Please sign in to comment.