Skip to content
This repository has been archived by the owner on Aug 22, 2019. It is now read-only.

Domain API endpoint #679

Merged
merged 11 commits into from Jul 5, 2018
Merged

Domain API endpoint #679

merged 11 commits into from Jul 5, 2018

Conversation

JoeyFaulkner
Copy link
Contributor

@JoeyFaulkner JoeyFaulkner commented Jun 29, 2018

Proposed changes:

  • create domain from yaml string
  • create yaml string from domain
  • expose endpoint for fetching domain yaml

Status (please check what you already did):

  • made PR ready for code review
  • added some tests for the functionality
  • updated the documentation
  • updated the changelog

@JoeyFaulkner JoeyFaulkner requested review from tmbo and amn41 June 29, 2018 12:47
Copy link
Contributor

@amn41 amn41 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think it's OK - but maybe we should think about persisting domains in saved models as yaml as well then rather than converting to json

utter_greet:
- text: hey there!"""
domain = TemplateDomain.load_from_yaml(test_yaml)
assert test_yaml in domain.to_yaml()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 'in' ? would x.strip() == y.strip() also work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, that works fine!

@@ -17,6 +17,10 @@
from builtins import input, range, str
from numpy import all, array
from typing import Text, Any, List, Optional, Tuple, Dict, Set
try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this a py2 / py3 thing? can we use six instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is, use six how?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw your other comment, ignore me!

@requires_auth(auth_token)
@ensure_loaded_agent(agent)
def get_domain_yaml():
"""Use a list of events to set a conversations tracker to a state."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description needs updated

action_factory = data.get("action_factory", None)
slots = cls.collect_slots(data.get("slots", {}))
additional_arguments = data.get("config", {})
return TemplateDomain(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have both cls and TemplateDomain in this method? surely they are the same? if we are allowing for subclassing then probably stick with cls ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yes, good call

@@ -319,6 +323,21 @@ def read_yaml_file(filename):

return yaml_parser.load(read_file(filename))

def read_yaml_string(string):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we then rewrite read_yaml_file to just be read_yaml_string(read_file(filename)) ?

@@ -319,6 +323,21 @@ def read_yaml_file(filename):

return yaml_parser.load(read_file(filename))

def read_yaml_string(string):

if six.PY2:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here an example of using six

@@ -416,7 +416,27 @@ def load(cls, filename, action_factory=None):
)

@classmethod
def validate_domain_yaml(cls, filename):
def load_from_yaml(cls, yaml, action_factory=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can load use load_from_yaml once it read the yaml from the file as well? want to avoid the duplication

def dump_obj_as_yaml_to_string(obj):
"""Writes data (python dict) to a yaml string."""

if six.PY2:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two functions should share the code, eg:

def _dump_yaml(obj, output):
    #...
    yaml_writer.dump(obj, output)
    #...

def dump_obj_as_yaml_to_file(filename, obj):
    with io.open(filename, 'w', encoding="utf-8") as yaml_file:
        _dump_yaml(obj, yaml_file)

def dump_obj_as_yaml(obj):
    str_io = StringIO()
    _dump_yaml(obj, str_io)
   return str_io.getvalue()

@ensure_loaded_agent(agent)
def get_domain_yaml():
"""Get current domain in yaml format."""
domain_yaml = agent().domain.to_yaml()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we sending the domain as yaml instead of json?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean its the way that the domain is stored in the model folder so why not?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a REST api, so usually the responses are in json format. I mean I don't have a strong opinion on this, but if yaml is used, content negotiation should be used: e.g.

accepts = request.headers.get("Accept", default="application/json")
if accepts.endswith("json"):
  domain = agent().domain.as_dict()
  return jsonify(domain)
elif accepts.endswith("yml"):
  domain = agent().domain.as_yaml()
  return Response(domain_yaml, status=200, content_type="application/x-yml")
else:
  return Response("""Invalid accept header. Domain can be provided as json ("Accept: application/json") or yml ("Accept: application/x-yml"). Make sure you've set the appropriate Accept header.""", status=406)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok cool!

)

@classmethod
def validate_domain_yaml(cls, filename):
def validate_domain_yaml(cls, input, string_input=False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input needs to be renamed (shadows build in function)

do we need string_input? I think it is always True

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it wasnt in tests but i changed tests :)

@@ -496,7 +504,7 @@ def instantiate_actions(factory_name, action_classes, action_names,
def _slot_definitions(self):
return {slot.name: slot.persistence_info() for slot in self.slots}

def persist(self, filename):
def _make_domain_data(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def as_dict(self) (for consistency across classes)

utils.dump_obj_as_yaml_to_file(filename, domain_data)

def to_yaml(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as_yaml for consistency

@ensure_loaded_agent(agent)
def get_domain_yaml():
"""Get current domain in yaml format."""
domain_yaml = agent().domain.to_yaml()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a REST api, so usually the responses are in json format. I mean I don't have a strong opinion on this, but if yaml is used, content negotiation should be used: e.g.

accepts = request.headers.get("Accept", default="application/json")
if accepts.endswith("json"):
  domain = agent().domain.as_dict()
  return jsonify(domain)
elif accepts.endswith("yml"):
  domain = agent().domain.as_yaml()
  return Response(domain_yaml, status=200, content_type="application/x-yml")
else:
  return Response("""Invalid accept header. Domain can be provided as json ("Accept: application/json") or yml ("Accept: application/x-yml"). Make sure you've set the appropriate Accept header.""", status=406)

Copy link
Member

@tmbo tmbo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small change left that needs to be done. also, please make sure to add your change to CHANGELOG.rst

)

@classmethod
def validate_domain_yaml(cls, filename):
def validate_domain_yaml(cls, input):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still needs to be renamed to something that is not input (shadows built in)

@JoeyFaulkner JoeyFaulkner merged commit 5eae121 into master Jul 5, 2018
@JoeyFaulkner JoeyFaulkner deleted the domain_endpoint branch July 5, 2018 09:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants