Index: client/libs/logdog/streamname.py |
diff --git a/client/libs/logdog/streamname.py b/client/libs/logdog/streamname.py |
index 8aaffb830b84db23fd0fdc31f93bec56ee7087cf..5cd3b3144ef3ea38815fff2629d771bbd3a88ad6 100644 |
--- a/client/libs/logdog/streamname.py |
+++ b/client/libs/logdog/streamname.py |
@@ -2,9 +2,12 @@ |
# Use of this source code is governed under the Apache License, Version 2.0 |
# that can be found in the LICENSE file. |
+import collections |
import re |
import string |
import types |
+import urllib |
+import urlparse |
_ALNUM_CHARS = string.ascii_letters + string.digits |
_SEGMENT_RE_BASE = r'[a-zA-Z0-9][a-zA-Z0-9:_\-.]*' |
@@ -105,3 +108,81 @@ def _is_valid_stream_char(ch, first=False): |
# Check additional middle-name characters: |
return ch in ':_-./' |
+ |
+ |
+class StreamPath(collections.namedtuple('_StreamPath', ('prefix', 'name'))): |
+ """StreamPath is a full stream path. |
+ |
+ This consists of both a stream prefix and a stream name. |
+ |
+ When constructed with parse or make, the stream path must be completely valid. |
+ However, invalid stream paths may be constructed by manually instantiation. |
+ This can be useful for wildcard query values (e.g., "prefix='foo/*/bar/**'"). |
+ """ |
+ |
+ @classmethod |
+ def make(cls, prefix, name): |
+ """Returns (StreamPath): The validated StreamPath instance. |
+ |
+ Args: |
+ prefix (str): the prefix component |
+ name (str): the name component |
+ |
+ Raises: |
+ ValueError: If path is not a full, valid stream path string. |
+ """ |
+ inst = cls(prefix=prefix, name=name) |
+ inst.validate() |
+ return inst |
+ |
+ @classmethod |
+ def parse(cls, path): |
+ """Returns (StreamPath): The parsed StreamPath instance. |
+ |
+ Args: |
+ path (str): the full stream path to parse. |
+ |
+ Raises: |
+ ValueError: If path is not a full, valid stream path string. |
+ """ |
+ parts = path.split('/+/', 1) |
+ if len(parts) != 2: |
+ raise ValueError('Not a full stream path: [%s]' % (path,)) |
+ return cls.make(*parts) |
+ |
+ def validate(self): |
+ """Raises: ValueError if this is not a valid stream name.""" |
+ try: |
+ validate_stream_name(self.prefix) |
+ except ValueError as e: |
+ raise ValueError('Invalid prefix component [%s]: %s' % ( |
+ self.prefix, e.message,)) |
+ |
+ try: |
+ validate_stream_name(self.name) |
+ except ValueError as e: |
+ raise ValueError('Invalid name component [%s]: %s' % ( |
+ self.name, e.message,)) |
+ |
+ def __str__(self): |
+ return '%s/+/%s' % (self.prefix, self.name) |
+ |
+ |
+def get_logdog_viewer_url(host, project, *stream_paths): |
+ """Returns (str): The LogDog viewer URL for the named stream(s). |
+ |
+ Args: |
+ host (str): The name of the Coordiantor host. |
+ project (str): The project name. |
+ stream_paths: A set of StreamPath instances for the stream paths to |
+ generate the URL for. |
+ """ |
+ return urlparse.urlunparse(( |
+ 'https', # Scheme |
+ host, # netloc |
+ 'v/', # path |
+ '', # params |
+ '&'.join(('s=%s' % (urllib.quote('%s/%s' % (project, path), safe='')) |
+ for path in stream_paths)), # query |
+ '', # fragment |
+ )) |