Skip to content
This repository has been archived by the owner on Jan 23, 2021. It is now read-only.

Commit

Permalink
Feature/read from file (#6)
Browse files Browse the repository at this point in the history
* Split  lint coverage and test

* Support reply read from file
  • Loading branch information
ahelal authored Aug 30, 2017
1 parent f5931a7 commit b030153
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 27 deletions.
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ install:
- "pip install -r requirements.txt"
- "pip install nose coverage pylint"

script: make tests-detail
script:
- make lint
- make detailed-tests
- make coverage
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ tests:
$(info $(M) Running tests for )
@PYTHONPATH="src/lib" nosetests -w "src/tests"

tests-detail:
detailed-tests:
$(info $(M) Running tests for $(VERSION))
@PYTHONPATH="src/lib" nosetests --detailed-errors -w "src/tests" -vv --nocapture \
--with-coverage --cover-package=base,check_op,in_op,out_op,payload
@PYTHONPATH="src/lib" nosetests --detailed-errors -w "src/tests" -vv --nocapture

coverage:
@coverage report -m src/lib/*.py

lint:
@pylint src/lib/*.py

push:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ resource_types :

* `grammar`: *Optional.* If not defined bender will respond to all mentions `@bot_name` and If grammar is defined bender will **only** respond to messages matching the regex expression. Use [python regular expression](https://docs.python.org/2/library/re.html) syntax. See [examples](examples.md) page for inspiration.

* `template`: *Optional*. A string that will be evaluated and written to `template_filename` can be used as an input file for further jobs in the pipeline.
* `template`: *Optional*. A **string** that will be evaluated and written to `template_filename` can be used as an input file for further jobs in the pipeline.

* `template_filename`: *Optional*, *default `template_file.txt`*. The file name for a generated template.

Expand Down Expand Up @@ -61,7 +61,7 @@ Replies with a message to the selected `channel`.

#### `out Parameters`

* `reply`: *Required*. The message to be used as reply. Supports [template format](#template).
* `reply`: *Required*. A **string** or **file path** to be used as reply. Supports [template format](#template).

* `reply_thread`: *optional*, *default `False`*. If enabled will post reply to original message as a thread.

Expand Down Expand Up @@ -98,7 +98,7 @@ The template uses python [Jinja2](http://jinja.pocoo.org/docs/2.9/) engine.

## TODO

* Support reading from a file for `template` and `reply`
* Add message username initiator to template engine.
* Refactor and simplify logic of check_logic_unread
* Increase code coverage

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.4
0.0.5
19 changes: 18 additions & 1 deletion src/lib/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ def write_to_file(content, output_file):
output_file_fd = open(output_file, 'w')
print(content, file=output_file_fd)

def read_if_exists(base_path, content):
''' Return content of file, if file exists else return content.'''
path = os.path.abspath(os.path.join(base_path, content))
is_file = os.path.isfile(path)
if not is_file:
return content

return read_content_from_file(path)

def read_content_from_file(path):
''' Return content of file.'''
try:
with open(path) as file_desc:
return file_desc.read()
except IOError as file_error:
fail_unless(False, "Failed to read file '{}'. IOError: '{}".format(path, file_error))


def fail_unless(condition, msg):
"""If condition is not True print msg and exit with status code 1"""
Expand All @@ -55,7 +72,7 @@ def __init__(self, **kwargs):
self.channel = kwargs.get("channel", "")
self.grammar = kwargs.get("grammar", False)
self.version = kwargs.get("version", False)
self.working_dir = kwargs.get("working_dir", False)
self.working_dir = kwargs.get("working_dir", "/")
self.slack_unread = kwargs.get("slack_unread", False)
# Get all user list
self.users = self._call_api("users.list", presence=0)
Expand Down
1 change: 1 addition & 0 deletions src/lib/in_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(self, **kwargs):
Base.__init__(self, **kwargs)
self.metadata = []
self.template = kwargs.get("template")

self.template_filename = os.path.basename(kwargs.get("template_filename", ""))
self.templated_string = None
self.original_msg = ""
Expand Down
25 changes: 15 additions & 10 deletions src/lib/out_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from __future__ import print_function

import json
import os

from payload import PayLoad
from base import Base, fail_unless, template_with_regex
from base import Base, fail_unless, template_with_regex, read_if_exists, read_content_from_file


class Out(Base):
Expand All @@ -16,9 +15,20 @@ def __init__(self, **kwargs):
Base.__init__(self, **kwargs)
self.metadata = []
self.path = kwargs.get("path", False)
self.reply = kwargs.get("reply", False)
self.reply = read_if_exists(self.working_dir, kwargs.get("reply", False))
self.reply_thread = kwargs.get("reply_thread", True)
self.bender_json_path = '{}/{}/bender.json'.format(self.working_dir, self.path)

# Get context from original message
context_json_path = '{}/{}/bender.json'.format(self.working_dir, self.path)
try:
context_content = read_content_from_file(context_json_path)
context_content = json.loads(context_content)
except ValueError as value_error:
fail_unless(False, "JSON Input error: {}".format(value_error))

self.version = context_content["version"]
self.metadata = context_content["metadata"]
self.original_msg = context_content["original_msg"]

def _reply(self, thread_timestamp, text):
args = {}
Expand All @@ -33,12 +43,7 @@ def _reply(self, thread_timestamp, text):
def out_logic(self):
"""Concourse resource `out` logic """

fail_unless(os.path.isfile(self.bender_json_path), "Failed to get version info from file {}".format(self.bender_json_path))
with open(self.bender_json_path) as bender_file:
output_data = json.load(bender_file)
self.version = output_data["version"]
self.metadata = output_data["metadata"]
regex = self._msg_grammar(output_data["original_msg"])
regex = self._msg_grammar(self.original_msg)
templated_reply = template_with_regex(self.reply, regex)

if self.reply_thread:
Expand Down
2 changes: 2 additions & 0 deletions src/tests/responses/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
content1
content2
13 changes: 13 additions & 0 deletions src/tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,16 @@ def test_fail_unless(self, mock_print):
self.assertEqual(value_error.exception.code, 1)
## Can't manage to check one argument only :(
#mock_print.assert_called_with("FIRE", file=mock.ANY, mode=mock.ANY)

@mock.patch('base.fail_unless')
def test_read_if_exists(self, mock_fail_unless):
base_path = "/Bcbc/x"
content = "SOMETHING"
return_val = base.read_if_exists(base_path, content)
self.assertEqual(return_val, "SOMETHING")
mock_fail_unless.assert_not_called()

content = "content1\ncontent2\n"
return_val = base.read_if_exists(os.path.dirname(__file__), "responses/file.txt")
mock_fail_unless.assert_not_called()
self.assertEqual(return_val, content)
3 changes: 2 additions & 1 deletion src/tests/test_in.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import json
import mock
import os
import sys
from unittest import TestCase

import mock

import in_op


Expand Down
15 changes: 8 additions & 7 deletions src/tests/test_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,32 @@

class OutTest(TestCase):

@mock.patch('out_op.read_content_from_file')
@mock.patch('out_op.Out._filter')
@mock.patch('out_op.Out._get_channel_group_info')
@mock.patch('out_op.Out._call_api')
def setUp(self, mock_call_api, mock_get_channel_group_info, mock_filter):

self.script_dir = os.path.dirname(__file__)
with open('{}/responses/users.json'.format(self.script_dir)) as f:
def setUp(self, mock_call_api, mock_get_channel_group_info, mock_filter, mock_read_content_from_file):
with open('{}/responses/users.json'.format(os.path.dirname(__file__))) as f:
mock_call_api.return_value = json.load(f)
mock_get_channel_group_info.return_value = "C024BE91L", "channels"
mock_filter.return_value = "U01B12FDS"
mock_read_content_from_file.return_value = '{"version": 1, "metadata": [1], "original_msg": "HI"}'

self.grammar = "^(superApp)\s+(deploy)\s+(live|staging)\s+(\S+)($|\s+)"
self.grammar = "^(superApp)\\s+(deploy)\\s+(live|staging)\\s+(\\S+)($|\\s+)"
self.resource = out_op.Out(token="token", channel="testChannel", bot="theBender", working_dir="/tmp",
grammar=self.grammar, path="bender_path", reply="testing 1.2.3",
reply_thread="reply_thread")

def test__init__(self):
# Channel info
self.assertEqual(self.resource.bender_json_path, "/tmp/bender_path/bender.json")
self.assertEqual(self.resource.path, "bender_path")
self.assertEqual(self.resource.reply, "testing 1.2.3")
self.assertEqual(self.resource.reply_thread, "reply_thread")
self.assertEqual(self.resource.working_dir, "/tmp")

#self.bender_json_path = '{}/{}/bender.json'.format(self.working_dir, self.path)
self.assertEqual(self.resource.version, 1)
self.assertEqual(self.resource.metadata, [1])
self.assertEqual(self.resource.original_msg, "HI")

@mock.patch('out_op.json.dumps')
@mock.patch('out_op.print', create=True)
Expand Down

0 comments on commit b030153

Please sign in to comment.