Skip to content

Commit

Permalink
Merge branch 'main' into 961-oauth-providers-does-not-validate-scopes
Browse files Browse the repository at this point in the history
  • Loading branch information
talboren authored Mar 18, 2024
2 parents afdd475 + 5f5bb6f commit 0620f34
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 23 deletions.
8 changes: 8 additions & 0 deletions docs/cli/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
title: "Installation"
---
<Info>Missing an installation? submit a <a href="https://github.com/keephq/keep/issues/new?assignees=&labels=&projects=&template=use_case.md&title=">new installation</a> request and we will add it as soon as we can.</Info>

<Info>
We recommend to install Keep CLI with Python version 3.11 for optimal compatibility and performance.
This choice ensures seamless integration with all dependencies, including pyarrow, which currently does not support Python 3.12
</Info>

<Tip>Need Keep CLI on other versions? Feel free to contact us! </Tip>

## Clone and install (Option 1)

### Install
Expand Down
27 changes: 27 additions & 0 deletions docs/workflows/functions/last.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: "last(iterable)"
sidebarTitle: "last"
---

### Input

An iterable.

### Output

The last item of the iterable.

### Example

```yaml
actions:
- name: keep-slack
foreach: "{{steps.this.results}}"
condition:
- type: threshold
value: "keep.last(keep.split({{ foreach.value }}, ' '))"
# each line looks like:
# '2023-02-09 20:08:16,773 INFO: uvicorn.access -: 127.0.0.1:53948 - "GET /test2 HTTP/1.1" 503'
# where the "503" is the number of the
compare_to: 200
```
24 changes: 24 additions & 0 deletions docs/workflows/functions/lowercase.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: "string(string)"
sidebarTitle: "lowercase"
---

### Input

A string.

### Output

Returns the string which is lowercased.

### Example

```yaml
actions:
- name: trigger-slack
condition:
- type: equals
value: keep.lowercase('ABC DEF')
compare_to: "abc def"
compare_type: eq
```
24 changes: 24 additions & 0 deletions docs/workflows/functions/uppercase.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: "string(string)"
sidebarTitle: "uppercase"
---

### Input

A string.

### Output

Returns the string which is uppercased.

### Example

```yaml
actions:
- name: trigger-slack
condition:
- type: equals
value: keep.uppercase('abc def')
compare_to: "ABC DEF"
compare_type: eq
```
16 changes: 16 additions & 0 deletions examples/workflows/autosupress.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
workflow:
id: autosupress
description: demonstrates how to automatically suppress alerts
triggers:
- type: alert
filters:
- key: name
value: r"(somename)"
actions:
- name: dismiss-alert
provider:
type: mock
with:
enrich_alert:
- key: dismissed
value: "true"
2 changes: 2 additions & 0 deletions keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,8 @@ def get_alerts_with_filters(tenant_id, provider_id=None, filters=None) -> list[A
if provider_id:
query = query.filter(Alert.provider_id == provider_id)

query = query.order_by(Alert.timestamp.desc())

# Execute the query
alerts = query.all()

Expand Down
13 changes: 10 additions & 3 deletions keep/api/routes/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,20 @@ def run_workflow(
# Finally, run it
try:
# if its event that was triggered by the UI with the Modal
if "test-workflow" in body.get("fingerprint", ""):
if "test-workflow" in body.get("fingerprint", "") or not body:
# some random
body["id"] = body.get("fingerprint")
body["id"] = body.get("fingerprint", "manual-run")
body["name"] = body.get("fingerprint", "manual-run")
body["lastReceived"] = datetime.datetime.now(
tz=datetime.timezone.utc
).isoformat()
alert = AlertDto(**body)
try:
alert = AlertDto(**body)
except TypeError:
raise HTTPException(
status_code=400,
detail="Invalid alert format",
)
workflow_execution_id = workflowmanager.scheduler.handle_manual_event_workflow(
workflow_id, tenant_id, created_by, alert
)
Expand Down
18 changes: 7 additions & 11 deletions keep/functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,38 @@ def all(iterable) -> bool:
g = groupby(iterable)
return next(g, True) and not next(g, False)


def diff(iterable: iter) -> bool:
# Opposite of all - returns True if any element is different
return not all(iterable)


def len(iterable=[]) -> int:
return _len(iterable)

def uppercase(string) -> str:
return string.upper()

def lowercase(string) -> str:
return string.lower()

def split(string, delimeter) -> list:
return string.strip().split(delimeter)


def strip(string) -> str:
return string.strip()


def first(iterable):
return iterable[0]

def last(iterable):
return iterable[-1]

def utcnow() -> datetime.datetime:
dt = datetime.datetime.now(datetime.timezone.utc)
return dt


def utcnowiso() -> str:
return utcnow().isoformat()


def substract_minutes(dt: datetime.datetime, minutes: int) -> datetime.datetime:
"""
Substract minutes from a datetime object
Expand All @@ -59,28 +60,23 @@ def substract_minutes(dt: datetime.datetime, minutes: int) -> datetime.datetime:
"""
return dt - datetime.timedelta(minutes=minutes)


def to_utc(dt: datetime.datetime | str) -> datetime.datetime:
if isinstance(dt, str):
dt = parser.parse(dt)
utc_dt = dt.astimezone(pytz.utc)
return utc_dt


def datetime_compare(t1, t2) -> float:
diff = (t1 - t2).total_seconds() / 3600
return diff


def json_dumps(data: str | dict) -> str:
if isinstance(data, str):
data = json.loads(data)
return json.dumps(data, indent=4, default=str)


def encode(string) -> str:
return urllib.parse.quote(string)


def dict_to_key_value_list(d: dict) -> list:
return [f"{k}:{v}" for k, v in d.items()]
7 changes: 6 additions & 1 deletion keep/providers/keep_provider/keep_provider.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Keep Provider is a class that allows to ingest/digest data from Keep.
"""

import logging
from typing import Optional

Expand All @@ -27,21 +28,25 @@ def dispose(self):
"""
pass

def _query(self, filters, **kwargs):
def _query(self, filters, distinct=True, **kwargs):
"""
Query Keep for alerts.
"""
db_alerts = get_alerts_with_filters(
self.context_manager.tenant_id, filters=filters
)

fingerprints = {}
alerts = []
if db_alerts:
for alert in db_alerts:
if fingerprints.get(alert.fingerprint) and distinct is True:
continue
alert_event = alert.event
if alert.alert_enrichment:
alert_event["enrichments"] = alert.alert_enrichment.enrichments
alerts.append(alert_event)
fingerprints[alert.fingerprint] = True
return alerts

def validate_config(self):
Expand Down
8 changes: 8 additions & 0 deletions keep/providers/mock_provider/mock_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def _query(self, **kwargs):
"""
return kwargs.get("command_output")

def _notify(self, **kwargs):
"""This is mock provider that just return the command output.
Returns:
_type_: _description_
"""
return kwargs

def dispose(self):
"""
No need to dispose of anything, so just do nothing.
Expand Down
25 changes: 17 additions & 8 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,56 +32,68 @@
("empty list", [], False),
],
)

def test_functions_diff(test_description, given, expected):
assert (
functions.diff(given) == expected
), f"{test_description}: Expected {given} to return {expected}"


def test_keep_len_function():
"""
Test the len function
"""
assert functions.len([1, 2, 3]) == 3


def test_keep_all_function():
"""
Test the all function
"""
assert functions.all([1, 1, 1]) == True
assert functions.all([1, 1, 0]) == False


def test_keep_diff_function():
"""
Test the diff function
"""
assert functions.diff([1, 1, 1]) == False
assert functions.diff([1, 1, 0]) == True


def test_keep_split_function():
"""
Test the split function
"""
assert functions.split("a,b,c", ",") == ["a", "b", "c"]
assert functions.split("a|b|c", "|") == ["a", "b", "c"]

def test_keep_uppercase_function():
"""
Test the uppercase function
"""
assert functions.uppercase("a") == "A"

def test_keep_lowercase_function():
"""
Test the lowercase function
"""
assert functions.lowercase("A") == "a"

def test_keep_strip_function():
"""
Test the strip function
"""
assert functions.strip(" a ") == "a"


def test_keep_first_function():
"""
Test the first function
"""
assert functions.first([1, 2, 3]) == 1

def test_keep_last_function():
"""
Test the last function
"""
assert functions.last([1, 2, 3]) == 3

def test_keep_utcnow_function():
"""
Expand All @@ -91,7 +103,6 @@ def test_keep_utcnow_function():
assert isinstance(dt.tzinfo, type(datetime.timezone.utc))
assert isinstance(dt, datetime.datetime)


def test_keep_to_utc_function():
"""
Test the to_utc function
Expand All @@ -103,7 +114,6 @@ def test_keep_to_utc_function():
now_utc = functions.to_utc(now)
assert now_utc.tzinfo == pytz.utc


def test_keep_datetime_compare_function():
"""
Test the datetime_compare function
Expand All @@ -114,7 +124,6 @@ def test_keep_datetime_compare_function():
assert int(functions.datetime_compare(dt2, dt1)) == 1
assert int(functions.datetime_compare(dt1, dt1)) == 0


def test_keep_encode_function():
"""
Test the encode function
Expand Down

0 comments on commit 0620f34

Please sign in to comment.