http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/element.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/element.py b/wave/src/main/java/python/api/element.py deleted file mode 100644 index 42e88b6..0000000 --- a/wave/src/main/java/python/api/element.py +++ /dev/null @@ -1,370 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Elements are non-text bits living in blips like images, gadgets etc. - -This module defines the Element class and the derived classes. -""" - - -import base64 -import logging -import sys - -import util - - -class Element(object): - """Elements are non-text content within a document. - - These are generally abstracted from the Robot. Although a Robot can query the - properties of an element it can only interact with the specific types that - the element represents. - - Properties of elements are both accessible directly (image.url) and through - the get method (image.get('url')). In general, Element - should not be instantiated by robots, but rather rely on the derived classes. - """ - - # INLINE_BLIP_TYPE is not a separate type since it shouldn't be instantiated, - # only be used for introspection - INLINE_BLIP_TYPE = "INLINE_BLIP" - - def __init__(self, element_type, **properties): - """Initializes self with the specified type and any properties. - - Args: - element_type: string typed member of ELEMENT_TYPE - properties: either a dictionary of initial properties, or a dictionary - with just one member properties that is itself a dictionary of - properties. This allows us to both use - e = Element(atype, prop1=val1, prop2=prop2...) - and - e = Element(atype, properties={prop1:val1, prop2:prop2..}) - """ - if len(properties) == 1 and 'properties' in properties: - properties = properties['properties'] - self._type = element_type - # as long as the operation_queue of an element in None, it is - # unattached. After an element is acquired by a blip, the blip - # will set the operation_queue to make sure all changes to the - # element are properly send to the server. - self._operation_queue = None - self._properties = properties.copy() - - @property - def type(self): - """The type of this element.""" - return self._type - - @classmethod - def from_json(cls, json): - """Class method to instantiate an Element based on a json string.""" - etype = json['type'] - props = json['properties'].copy() - - element_class = ALL.get(etype) - if not element_class: - # Unknown type. Server could be newer than we are - return Element(element_type=etype, properties=props) - - return element_class.from_props(props) - - def get(self, key, default=None): - """Standard get interface.""" - return self._properties.get(key, default) - - def __getattr__(self, key): - return self._properties[key] - - def serialize(self): - """Custom serializer for Elements.""" - return util.serialize({'properties': util.non_none_dict(self._properties), - 'type': self._type}) - - -class Input(Element): - """A single-line input element.""" - - class_type = 'INPUT' - - def __init__(self, name, value=''): - super(Input, self).__init__(Input.class_type, - name=name, - value=value, - default_value=value) - - @classmethod - def from_props(cls, props): - return Input(name=props.get('name'), value=props.get('value')) - - -class Check(Element): - """A checkbox element.""" - - class_type = 'CHECK' - - def __init__(self, name, value=''): - super(Check, self).__init__(Check.class_type, - name=name, value=value, default_value=value) - - @classmethod - def from_props(cls, props): - return Check(name=props.get('name'), value=props.get('value')) - - -class Button(Element): - """A button element.""" - - class_type = 'BUTTON' - - def __init__(self, name, value): - super(Button, self).__init__(Button.class_type, - name=name, value=value) - - @classmethod - def from_props(cls, props): - return Button(name=props.get('name'), value=props.get('value')) - - -class Label(Element): - """A label element.""" - - class_type = 'LABEL' - - def __init__(self, label_for, caption): - super(Label, self).__init__(Label.class_type, - name=label_for, value=caption) - - @classmethod - def from_props(cls, props): - return Label(label_for=props.get('name'), caption=props.get('value')) - - -class RadioButton(Element): - """A radio button element.""" - - class_type = 'RADIO_BUTTON' - - def __init__(self, name, group): - super(RadioButton, self).__init__(RadioButton.class_type, - name=name, value=group) - - @classmethod - def from_props(cls, props): - return RadioButton(name=props.get('name'), group=props.get('value')) - - -class RadioButtonGroup(Element): - """A group of radio buttons.""" - - class_type = 'RADIO_BUTTON_GROUP' - - def __init__(self, name, value): - super(RadioButtonGroup, self).__init__(RadioButtonGroup.class_type, - name=name, value=value) - - @classmethod - def from_props(cls, props): - return RadioButtonGroup(name=props.get('name'), value=props.get('value')) - - -class Password(Element): - """A password element.""" - - class_type = 'PASSWORD' - - def __init__(self, name, value): - super(Password, self).__init__(Password.class_type, - name=name, value=value) - - @classmethod - def from_props(cls, props): - return Password(name=props.get('name'), value=props.get('value')) - - -class TextArea(Element): - """A text area element.""" - - class_type = 'TEXTAREA' - - def __init__(self, name, value): - super(TextArea, self).__init__(TextArea.class_type, - name=name, value=value) - - @classmethod - def from_props(cls, props): - return TextArea(name=props.get('name'), value=props.get('value')) - - -class Line(Element): - """A line element. - - Note that Lines are represented in the text as newlines. - """ - - class_type = 'LINE' - - # Possible line types: - #: Designates line as H1, largest heading. - TYPE_H1 = 'h1' - #: Designates line as H2 heading. - TYPE_H2 = 'h2' - #: Designates line as H3 heading. - TYPE_H3 = 'h3' - #: Designates line as H4 heading. - TYPE_H4 = 'h4' - #: Designates line as H5, smallest heading. - TYPE_H5 = 'h5' - #: Designates line as a bulleted list item. - TYPE_LI = 'li' - - # Possible values for align - #: Sets line alignment to left. - ALIGN_LEFT = 'l' - #: Sets line alignment to right. - ALIGN_RIGHT = 'r' - #: Sets line alignment to centered. - ALIGN_CENTER = 'c' - #: Sets line alignment to justified. - ALIGN_JUSTIFIED = 'j' - - def __init__(self, - line_type=None, - indent=None, - alignment=None, - direction=None): - super(Line, self).__init__(Line.class_type, - lineType=line_type, - indent=indent, - alignment=alignment, - direction=direction) - - @classmethod - def from_props(cls, props): - return Line(line_type=props.get('lineType'), - indent=props.get('indent'), - alignment=props.get('alignment'), - direction=props.get('direction')) - - -class Gadget(Element): - """A gadget element.""" - - class_type = 'GADGET' - - def __init__(self, url, props=None): - if props is None: - props = {} - props['url'] = url - super(Gadget, self).__init__(Gadget.class_type, properties=props) - - @classmethod - def from_props(cls, props): - return Gadget(props.get('url'), props) - - def serialize(self): - """Gadgets allow for None values.""" - return {'properties': self._properties, 'type': self._type} - - def keys(self): - """Get the valid keys for this gadget.""" - return [x for x in self._properties.keys() if x != 'url'] - - -class Installer(Element): - """An installer element.""" - - class_type = 'INSTALLER' - - def __init__(self, manifest): - super(Installer, self).__init__(Installer.class_type, manifest=manifest) - - @classmethod - def from_props(cls, props): - return Installer(props.get('manifest')) - - - -class Image(Element): - """An image element.""" - - class_type = 'IMAGE' - - def __init__(self, url='', width=None, height=None, - attachmentId=None, caption=None): - super(Image, self).__init__(Image.class_type, url=url, width=width, - height=height, attachmentId=attachmentId, caption=caption) - - @classmethod - def from_props(cls, props): - props = dict([(key.encode('utf-8'), value) - for key, value in props.items()]) - return apply(Image, [], props) - -class Attachment(Element): - """An attachment element. - - To create a new attachment, caption and data are needed. - mimeType, attachmentId and attachmentUrl are sent via events. - """ - - class_type = 'ATTACHMENT' - - def __init__(self, caption=None, data=None, mimeType=None, attachmentId=None, - attachmentUrl=None): - Attachment.originalData = data - super(Attachment, self).__init__(Attachment.class_type, caption=caption, - data=data, mimeType=mimeType, attachmentId=attachmentId, - attachmentUrl=attachmentUrl) - - def __getattr__(self, key): - if key and key == 'data': - return Attachment.originalData - return super(Attachment, self).__getattr__(key) - - @classmethod - def from_props(cls, props): - props = dict([(key.encode('utf-8'), value) - for key, value in props.items()]) - return apply(Attachment, [], props) - - def serialize(self): - """Serializes the attachment object into JSON. - - The attachment data is base64 encoded. - """ - - if self.data: - self._properties['data'] = base64.encodestring(self.data) - return super(Attachment, self).serialize() - - -def is_element(cls): - """Returns whether the passed class is an element.""" - try: - if not issubclass(cls, Element): - return False - h = hasattr(cls, 'class_type') - return hasattr(cls, 'class_type') - except TypeError: - return False - -ALL = dict([(item.class_type, item) for item in globals().copy().values() - if is_element(item)])
http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/element_test.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/element_test.py b/wave/src/main/java/python/api/element_test.py deleted file mode 100644 index f1be868..0000000 --- a/wave/src/main/java/python/api/element_test.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python2.4 -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Unit tests for the element module.""" - - -import base64 -import unittest - -import element -import util - - -class TestElement(unittest.TestCase): - """Tests for the element.Element class.""" - - def testProperties(self): - el = element.Element(element.Gadget.class_type, - key='value') - self.assertEquals('value', el.key) - - def testFormElement(self): - el = element.Input('input') - self.assertEquals(element.Input.class_type, el.type) - self.assertEquals(el.value, '') - self.assertEquals(el.name, 'input') - - def testImage(self): - image = element.Image('http://test.com/image.png', width=100, height=100) - self.assertEquals(element.Image.class_type, image.type) - self.assertEquals(image.url, 'http://test.com/image.png') - self.assertEquals(image.width, 100) - self.assertEquals(image.height, 100) - - def testAttachment(self): - attachment = element.Attachment(caption='My Favorite', data='SomefakeData') - self.assertEquals(element.Attachment.class_type, attachment.type) - self.assertEquals(attachment.caption, 'My Favorite') - self.assertEquals(attachment.data, 'SomefakeData') - - def testGadget(self): - gadget = element.Gadget('http://test.com/gadget.xml') - self.assertEquals(element.Gadget.class_type, gadget.type) - self.assertEquals(gadget.url, 'http://test.com/gadget.xml') - - def testInstaller(self): - installer = element.Installer('http://test.com/installer.xml') - self.assertEquals(element.Installer.class_type, installer.type) - self.assertEquals(installer.manifest, 'http://test.com/installer.xml') - - def testSerialize(self): - image = element.Image('http://test.com/image.png', width=100, height=100) - s = util.serialize(image) - k = s.keys() - k.sort() - # we should really only have three things to serialize - props = s['properties'] - self.assertEquals(len(props), 3) - self.assertEquals(props['url'], 'http://test.com/image.png') - self.assertEquals(props['width'], 100) - self.assertEquals(props['height'], 100) - - def testSerializeAttachment(self): - attachment = element.Attachment(caption='My Favorite', data='SomefakeData') - s = util.serialize(attachment) - k = s.keys() - k.sort() - # we should really have two things to serialize - props = s['properties'] - self.assertEquals(len(props), 2) - self.assertEquals(props['caption'], 'My Favorite') - self.assertEquals(props['data'], base64.encodestring('SomefakeData')) - self.assertEquals(attachment.data, 'SomefakeData') - - def testSerializeLine(self): - line = element.Line(element.Line.TYPE_H1, alignment=element.Line.ALIGN_LEFT) - s = util.serialize(line) - k = s.keys() - k.sort() - # we should really only have three things to serialize - props = s['properties'] - self.assertEquals(len(props), 2) - self.assertEquals(props['alignment'], 'l') - self.assertEquals(props['lineType'], 'h1') - - def testSerializeGadget(self): - gadget = element.Gadget('http://test.com', {'prop1': 'a', 'prop_cap': None}) - s = util.serialize(gadget) - k = s.keys() - k.sort() - # we should really only have three things to serialize - props = s['properties'] - self.assertEquals(len(props), 3) - self.assertEquals(props['url'], 'http://test.com') - self.assertEquals(props['prop1'], 'a') - self.assertEquals(props['prop_cap'], None) - - def testGadgetElementFromJson(self): - url = 'http://www.foo.com/gadget.xml' - json = { - 'type': element.Gadget.class_type, - 'properties': { - 'url': url, - } - } - gadget = element.Element.from_json(json) - self.assertEquals(element.Gadget.class_type, gadget.type) - self.assertEquals(url, gadget.url) - - def testImageElementFromJson(self): - url = 'http://www.foo.com/image.png' - width = '32' - height = '32' - attachment_id = '2' - caption = 'Test Image' - json = { - 'type': element.Image.class_type, - 'properties': { - 'url': url, - 'width': width, - 'height': height, - 'attachmentId': attachment_id, - 'caption': caption, - } - } - image = element.Element.from_json(json) - self.assertEquals(element.Image.class_type, image.type) - self.assertEquals(url, image.url) - self.assertEquals(width, image.width) - self.assertEquals(height, image.height) - self.assertEquals(attachment_id, image.attachmentId) - self.assertEquals(caption, image.caption) - - def testAttachmentElementFromJson(self): - caption = 'fake caption' - data = 'fake data' - mime_type = 'fake mime' - attachment_id = 'fake id' - attachment_url = 'fake URL' - json = { - 'type': element.Attachment.class_type, - 'properties': { - 'caption': caption, - 'data': data, - 'mimeType': mime_type, - 'attachmentId': attachment_id, - 'attachmentUrl': attachment_url, - } - } - attachment = element.Element.from_json(json) - self.assertEquals(element.Attachment.class_type, attachment.type) - self.assertEquals(caption, attachment.caption) - self.assertEquals(data, attachment.data) - self.assertEquals(mime_type, attachment.mimeType) - self.assertEquals(attachment_id, attachment.attachmentId) - self.assertEquals(attachment_url, attachment.attachmentUrl) - - def testFormElementFromJson(self): - name = 'button' - value = 'value' - default_value = 'foo' - json = { - 'type': element.Label.class_type, - 'properties': { - 'name': name, - 'value': value, - 'defaultValue': default_value, - } - } - el = element.Element.from_json(json) - self.assertEquals(element.Label.class_type, el.type) - self.assertEquals(name, el.name) - self.assertEquals(value, el.value) - - def testCanInstantiate(self): - bag = [element.Check(name='check', value='value'), - element.Button(name='button', value='caption'), - element.Input(name='input', value='caption'), - element.Label(label_for='button', caption='caption'), - element.RadioButton(name='name', group='group'), - element.RadioButtonGroup(name='name', value='value'), - element.Password(name='name', value='geheim'), - element.TextArea(name='name', value='\n\n\n'), - element.Installer(manifest='test.com/installer.xml'), - element.Line(line_type='type', - indent='3', - alignment='r', - direction='d'), - element.Gadget(url='test.com/gadget.xml', - props={'key1': 'val1', 'key2': 'val2'}), - element.Image(url='test.com/image.png', width=100, height=200), - element.Attachment(caption='fake caption', data='fake data')] - types_constructed = set([type(x) for x in bag]) - types_required = set(element.ALL.values()) - missing_required = types_constructed.difference(types_required) - self.assertEquals(missing_required, set()) - missing_constructed = types_required.difference(types_constructed) - self.assertEquals(missing_constructed, set()) - - -if __name__ == '__main__': - unittest.main() http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/errors.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/errors.py b/wave/src/main/java/python/api/errors.py deleted file mode 100644 index 89ca0a5..0000000 --- a/wave/src/main/java/python/api/errors.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Contains various API-specific exception classes. - -This module contains various specific exception classes that are raised by -the library back to the client. -""" - - -class Error(Exception): - """Base library error type.""" - -class RpcError(Error): - """Wave rpc error.""" http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/events.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/events.py b/wave/src/main/java/python/api/events.py deleted file mode 100644 index d63b16a..0000000 --- a/wave/src/main/java/python/api/events.py +++ /dev/null @@ -1,304 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Defines event types that are sent from the wave server. - -This module defines all of the event types currently supported by the wave -server. Each event type is sub classed from Event and has its own -properties depending on the type. -""" - - -class Context(object): - """Specifies constants representing different context requests.""" - - #: Requests the root blip. - ROOT = 'ROOT' - #: Requests the parent blip of the event blip. - PARENT = 'PARENT' - #: Requests the siblings blip of the event blip. - SIBLINGS = 'SIBLINGS' - #: Requests the child blips of the event blip. - CHILDREN = 'CHILDREN' - #: Requests the event blip itself. - SELF = 'SELF' - #: Requests all of the blips of the event wavelet. - ALL = 'ALL' - - -class Event(object): - """Object describing a single event. - - Attributes: - modified_by: Participant id that caused this event. - - timestamp: Timestamp that this event occurred on the server. - - type: Type string of this event. - - properties: Dictionary of all extra properties. Typically the derrived - event type should have these explicitly set as attributes, but - experimental features might appear in properties before that. - - blip_id: The blip_id of the blip for blip related events or the root - blip for wavelet related events. - - blip: If available, the blip with id equal to the events blip_id. - - proxying_for: If available, the proxyingFor id of the robot that caused the - event. - """ - - def __init__(self, json, wavelet): - """Inits this event with JSON data. - - Args: - json: JSON data from Wave server. - """ - self.modified_by = json.get('modifiedBy') - self.timestamp = json.get('timestamp', 0) - self.type = json.get('type') - self.raw_data = json - self.properties = json.get('properties', {}) - self.blip_id = self.properties.get('blipId') - self.blip = wavelet.blips.get(self.blip_id) - self.proxying_for = json.get('proxyingFor') - -class WaveletBlipCreated(Event): - """Event triggered when a new blip is created. - - Attributes: - new_blip_id: The id of the newly created blip. - - new_blip: If in context, the actual new blip. - """ - type = 'WAVELET_BLIP_CREATED' - - def __init__(self, json, wavelet): - super(WaveletBlipCreated, self).__init__(json, wavelet) - self.new_blip_id = self.properties['newBlipId'] - self.new_blip = wavelet.blips.get(self.new_blip_id) - - -class WaveletBlipRemoved(Event): - """Event triggered when a new blip is removed. - - Attributes: - removed_blip_id: the id of the removed blip - - removed_blip: if in context, the removed blip - """ - type = 'WAVELET_BLIP_REMOVED' - - def __init__(self, json, wavelet): - super(WaveletBlipRemoved, self).__init__(json, wavelet) - self.removed_blip_id = self.properties['removedBlipId'] - self.removed_blip = wavelet.blips.get(self.removed_blip_id) - - -class WaveletParticipantsChanged(Event): - """Event triggered when the participants on a wave change. - - Attributes: - participants_added: List of participants added. - - participants_removed: List of participants removed. - """ - type = 'WAVELET_PARTICIPANTS_CHANGED' - - def __init__(self, json, wavelet): - super(WaveletParticipantsChanged, self).__init__(json, wavelet) - self.participants_added = self.properties['participantsAdded'] - self.participants_removed = self.properties['participantsRemoved'] - - -class WaveletSelfAdded(Event): - """Event triggered when the robot is added to the wavelet.""" - type = 'WAVELET_SELF_ADDED' - - -class WaveletSelfRemoved(Event): - """Event triggered when the robot is removed from the wavelet.""" - type = 'WAVELET_SELF_REMOVED' - - -class WaveletTitleChanged(Event): - """Event triggered when the title of the wavelet has changed. - - Attributes: - title: The new title. - """ - type = 'WAVELET_TITLE_CHANGED' - - def __init__(self, json, wavelet): - super(WaveletTitleChanged, self).__init__(json, wavelet) - self.title = self.properties['title'] - - -class BlipContributorsChanged(Event): - """Event triggered when the contributors to this blip change. - - Attributes: - contributors_added: List of contributors that were added. - - contributors_removed: List of contributors that were removed. - """ - type = 'BLIP_CONTRIBUTORS_CHANGED' - - def __init__(self, json, wavelet): - super(BlipContributorsChanged, self).__init__(json, wavelet) - self.contibutors_added = self.properties['contributorsAdded'] - self.contibutors_removed = self.properties['contributorsRemoved'] - - -class BlipSubmitted(Event): - """Event triggered when a blip is submitted.""" - type = 'BLIP_SUBMITTED' - - -class DocumentChanged(Event): - """Event triggered when a document is changed. - - This event is fired after any changes in the document and should be used - carefully to keep the amount of traffic to the robot reasonable. Use - filters where appropriate. - """ - type = 'DOCUMENT_CHANGED' - - -class FormButtonClicked(Event): - """Event triggered when a form button is clicked. - - Attributes: - button_name: The name of the button that was clicked. - """ - type = 'FORM_BUTTON_CLICKED' - - def __init__(self, json, wavelet): - super(FormButtonClicked, self).__init__(json, wavelet) - self.button_name = self.properties['buttonName'] - - -class GadgetStateChanged(Event): - """Event triggered when the state of a gadget changes. - - Attributes: - index: The index of the gadget that changed in the document. - - old_state: The old state of the gadget. - """ - type = 'GADGET_STATE_CHANGED' - - def __init__(self, json, wavelet): - super(GadgetStateChanged, self).__init__(json, wavelet) - self.index = self.properties['index'] - self.old_state = self.properties['oldState'] - - -class AnnotatedTextChanged(Event): - """Event triggered when text with an annotation has changed. - - This is mainly useful in combination with a filter on the - name of the annotation. - - Attributes: - name: The name of the annotation. - - value: The value of the annotation that changed. - """ - type = 'ANNOTATED_TEXT_CHANGED' - - def __init__(self, json, wavelet): - super(AnnotatedTextChanged, self).__init__(json, wavelet) - self.name = self.properties['name'] - self.value = self.properties.get('value') - - -class OperationError(Event): - """Triggered when an event on the server occurred. - - Attributes: - operation_id: The operation id of the failing operation. - - error_message: More information as to what went wrong. - """ - type = 'OPERATION_ERROR' - - def __init__(self, json, wavelet): - super(OperationError, self).__init__(json, wavelet) - self.operation_id = self.properties['operationId'] - self.error_message = self.properties['message'] - - -class WaveletCreated(Event): - """Triggered when a new wavelet is created. - - This event is only triggered if the robot creates a new - wavelet and can be used to initialize the newly created wave. - wavelets created by other participants remain invisible - to the robot until the robot is added to the wave in - which case WaveletSelfAdded is triggered. - - Attributes: - message: Whatever string was passed into the new_wave - call as message (if any). - """ - type = 'WAVELET_CREATED' - - def __init__(self, json, wavelet): - super(WaveletCreated, self).__init__(json, wavelet) - self.message = self.properties['message'] - - -class WaveletFetched(Event): - """Triggered when a new wavelet is fetched. - - This event is triggered after a robot requests to - see another wavelet. The robot has to be on the other - wavelet already. - - Attributes: - message: Whatever string was passed into the new_wave - call as message (if any). - """ - type = 'WAVELET_FETCHED' - - def __init__(self, json, wavelet): - super(WaveletFetched, self).__init__(json, wavelet) - self.message = self.properties['message'] - - -class WaveletTagsChanged(Event): - """Event triggered when the tags on a wavelet change.""" - type = 'WAVELET_TAGS_CHANGED' - - def __init__(self, json, wavelet): - super(WaveletTagsChanged, self).__init__(json, wavelet) - - -def is_event(cls): - """Returns whether the passed class is an event.""" - try: - if not issubclass(cls, Event): - return False - return hasattr(cls, 'type') - except TypeError: - return False - -ALL = [item for item in globals().copy().values() if is_event(item)] http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/module_test_runner.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/module_test_runner.py b/wave/src/main/java/python/api/module_test_runner.py deleted file mode 100644 index 409519b..0000000 --- a/wave/src/main/java/python/api/module_test_runner.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Module defines the ModuleTestRunnerClass.""" - - -import unittest - - -class ModuleTestRunner(object): - """Responsible for executing all test cases in a list of modules.""" - - def __init__(self, module_list=None, module_test_settings=None): - self.modules = module_list or [] - self.settings = module_test_settings or {} - - def RunAllTests(self): - """Executes all tests present in the list of modules.""" - runner = unittest.TextTestRunner() - for module in self.modules: - for setting, value in self.settings.iteritems(): - try: - setattr(module, setting, value) - except AttributeError: - print '\nError running ' + str(setting) - print '\nRunning all tests in module', module.__name__ - runner.run(unittest.defaultTestLoader.loadTestsFromModule(module)) http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/oauth/LICENSE ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/oauth/LICENSE b/wave/src/main/java/python/api/oauth/LICENSE deleted file mode 100644 index 89f0591..0000000 --- a/wave/src/main/java/python/api/oauth/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License - -Copyright (c) 2007 Andy Smith - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/oauth/__init__.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/oauth/__init__.py b/wave/src/main/java/python/api/oauth/__init__.py deleted file mode 100644 index d69d952..0000000 --- a/wave/src/main/java/python/api/oauth/__init__.py +++ /dev/null @@ -1,541 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -# -import cgi -import urllib -import time -import random -import urlparse -import hmac -import base64 - -VERSION = '1.0' # Hi Blaine! -HTTP_METHOD = 'GET' -SIGNATURE_METHOD = 'PLAINTEXT' - -# Generic exception class -class OAuthError(RuntimeError): - def __init__(self, message='OAuth error occured.'): - self.message = message - -# optional WWW-Authenticate header (401 error) -def build_authenticate_header(realm=''): - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - -# url escape -def escape(s): - # escape '/' too - return urllib.quote(s, safe='~') - -# util function: current timestamp -# seconds since epoch (UTC) -def generate_timestamp(): - return int(time.time()) - -# util function: nonce -# pseudorandom number -def generate_nonce(length=8): - return ''.join(str(random.randint(0, 9)) for i in range(length)) - -# OAuthConsumer is a data type that represents the identity of the Consumer -# via its shared secret with the Service Provider. -class OAuthConsumer(object): - key = None - secret = None - - def __init__(self, key, secret): - self.key = key - self.secret = secret - -# OAuthToken is a data type that represents an End User via either an access -# or request token. -class OAuthToken(object): - # access tokens and request tokens - key = None - secret = None - - ''' - key = the token - secret = the token secret - ''' - def __init__(self, key, secret): - self.key = key - self.secret = secret - - def to_string(self): - return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) - - # return a token from something like: - # oauth_token_secret=digg&oauth_token=digg - @staticmethod - def from_string(s): - params = cgi.parse_qs(s, keep_blank_values=False) - key = params['oauth_token'][0] - secret = params['oauth_token_secret'][0] - return OAuthToken(key, secret) - - def __str__(self): - return self.to_string() - -# OAuthRequest represents the request and can be serialized -class OAuthRequest(object): - ''' - OAuth parameters: - - oauth_consumer_key - - oauth_token - - oauth_signature_method - - oauth_signature - - oauth_timestamp - - oauth_nonce - - oauth_version - ... any additional parameters, as defined by the Service Provider. - ''' - parameters = None # oauth parameters - http_method = HTTP_METHOD - http_url = None - version = VERSION - - def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): - self.http_method = http_method - self.http_url = http_url - self.parameters = parameters or {} - - def set_parameter(self, parameter, value): - self.parameters[parameter] = value - - def get_parameter(self, parameter): - try: - return self.parameters[parameter] - except: - raise OAuthError('Parameter not found: %s' % parameter) - - def _get_timestamp_nonce(self): - return self.get_parameter('oauth_timestamp'), self.get_parameter('oauth_nonce') - - # get any non-oauth parameters - def get_nonoauth_parameters(self): - parameters = {} - for k, v in self.parameters.iteritems(): - # ignore oauth parameters - if k.find('oauth_') < 0: - parameters[k] = v - return parameters - - # serialize as a header for an HTTPAuth request - def to_header(self, realm=''): - auth_header = 'OAuth realm="%s"' % realm - # add the oauth parameters - if self.parameters: - for k, v in self.parameters.iteritems(): - auth_header += ', %s="%s"' % (k, escape(str(v))) - return {'Authorization': auth_header} - - # serialize as post data for a POST request - def to_postdata(self): - return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems()) - - # serialize as a url for a GET request - def to_url(self): - return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) - - # return a string that consists of all the parameters that need to be signed - def get_normalized_parameters(self): - params = self.parameters - try: - # exclude the signature if it exists - del params['oauth_signature'] - except: - pass - key_values = params.items() - # sort lexicographically, first after key, then after value - key_values.sort() - # combine key value pairs in string and escape - return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in key_values) - - # just uppercases the http method - def get_normalized_http_method(self): - return self.http_method.upper() - - # parses the url and rebuilds it to be scheme://host/path - def get_normalized_http_url(self): - parts = urlparse.urlparse(self.http_url) - url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path - return url_string - - # set the signature parameter to the result of build_signature - def sign_request(self, signature_method, consumer, token): - # set the signature method - self.set_parameter('oauth_signature_method', signature_method.get_name()) - # set the signature - self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) - - def build_signature(self, signature_method, consumer, token): - # call the build signature method within the signature method - return signature_method.build_signature(self, consumer, token) - - @staticmethod - def from_request(http_method, http_url, headers=None, parameters=None, query_string=None): - # combine multiple parameter sources - if parameters is None: - parameters = {} - - # headers - if headers and 'Authorization' in headers: - auth_header = headers['Authorization'] - # check that the authorization header is OAuth - if auth_header.index('OAuth') > -1: - try: - # get the parameters from the header - header_params = OAuthRequest._split_header(auth_header) - parameters.update(header_params) - except: - raise OAuthError('Unable to parse OAuth parameters from Authorization header.') - - # GET or POST query string - if query_string: - query_params = OAuthRequest._split_url_string(query_string) - parameters.update(query_params) - - # URL parameters - param_str = urlparse.urlparse(http_url)[4] # query - url_params = OAuthRequest._split_url_string(param_str) - parameters.update(url_params) - - if parameters: - return OAuthRequest(http_method, http_url, parameters) - - return None - - @staticmethod - def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None): - if not parameters: - parameters = {} - - defaults = { - 'oauth_consumer_key': oauth_consumer.key, - 'oauth_timestamp': generate_timestamp(), - 'oauth_nonce': generate_nonce(), - 'oauth_version': OAuthRequest.version, - } - - defaults.update(parameters) - parameters = defaults - - if token: - parameters['oauth_token'] = token.key - - return OAuthRequest(http_method, http_url, parameters) - - @staticmethod - def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): - if not parameters: - parameters = {} - - parameters['oauth_token'] = token.key - - if callback: - parameters['oauth_callback'] = escape(callback) - - return OAuthRequest(http_method, http_url, parameters) - - # util function: turn Authorization: header into parameters, has to do some unescaping - @staticmethod - def _split_header(header): - params = {} - parts = header.split(',') - for param in parts: - # ignore realm parameter - if param.find('OAuth realm') > -1: - continue - # remove whitespace - param = param.strip() - # split key-value - param_parts = param.split('=', 1) - # remove quotes and unescape the value - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) - return params - - # util function: turn url string into parameters, has to do some unescaping - @staticmethod - def _split_url_string(param_str): - parameters = cgi.parse_qs(param_str, keep_blank_values=False) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) - return parameters - -# OAuthServer is a worker to check a requests validity against a data store -class OAuthServer(object): - timestamp_threshold = 300 # in seconds, five minutes - version = VERSION - signature_methods = None - data_store = None - - def __init__(self, data_store=None, signature_methods=None): - self.data_store = data_store - self.signature_methods = signature_methods or {} - - def set_data_store(self, oauth_data_store): - self.data_store = data_store - - def get_data_store(self): - return self.data_store - - def add_signature_method(self, signature_method): - self.signature_methods[signature_method.get_name()] = signature_method - return self.signature_methods - - # process a request_token request - # returns the request token on success - def fetch_request_token(self, oauth_request): - try: - # get the request token for authorization - token = self._get_token(oauth_request, 'request') - except OAuthError: - # no token required for the initial token request - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - self._check_signature(oauth_request, consumer, None) - # fetch a new token - token = self.data_store.fetch_request_token(consumer) - return token - - # process an access_token request - # returns the access token on success - def fetch_access_token(self, oauth_request): - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - # get the request token - token = self._get_token(oauth_request, 'request') - self._check_signature(oauth_request, consumer, token) - new_token = self.data_store.fetch_access_token(consumer, token) - return new_token - - # verify an api call, checks all the parameters - def verify_request(self, oauth_request): - # -> consumer and token - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - # get the access token - token = self._get_token(oauth_request, 'access') - self._check_signature(oauth_request, consumer, token) - parameters = oauth_request.get_nonoauth_parameters() - return consumer, token, parameters - - # authorize a request token - def authorize_token(self, token, user): - return self.data_store.authorize_request_token(token, user) - - # get the callback url - def get_callback(self, oauth_request): - return oauth_request.get_parameter('oauth_callback') - - # optional support for the authenticate header - def build_authenticate_header(self, realm=''): - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - - # verify the correct version request for this server - def _get_version(self, oauth_request): - try: - version = oauth_request.get_parameter('oauth_version') - except: - version = VERSION - if version and version != self.version: - raise OAuthError('OAuth version %s not supported.' % str(version)) - return version - - # figure out the signature with some defaults - def _get_signature_method(self, oauth_request): - try: - signature_method = oauth_request.get_parameter('oauth_signature_method') - except: - signature_method = SIGNATURE_METHOD - try: - # get the signature method object - signature_method = self.signature_methods[signature_method] - except: - signature_method_names = ', '.join(self.signature_methods.keys()) - raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) - - return signature_method - - def _get_consumer(self, oauth_request): - consumer_key = oauth_request.get_parameter('oauth_consumer_key') - if not consumer_key: - raise OAuthError('Invalid consumer key.') - consumer = self.data_store.lookup_consumer(consumer_key) - if not consumer: - raise OAuthError('Invalid consumer.') - return consumer - - # try to find the token for the provided request token key - def _get_token(self, oauth_request, token_type='access'): - token_field = oauth_request.get_parameter('oauth_token') - token = self.data_store.lookup_token(token_type, token_field) - if not token: - raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) - return token - - def _check_signature(self, oauth_request, consumer, token): - timestamp, nonce = oauth_request._get_timestamp_nonce() - self._check_timestamp(timestamp) - self._check_nonce(consumer, token, nonce) - signature_method = self._get_signature_method(oauth_request) - try: - signature = oauth_request.get_parameter('oauth_signature') - except: - raise OAuthError('Missing signature.') - # validate the signature - valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature) - if not valid_sig: - key, base = signature_method.build_signature_base_string(oauth_request, consumer, token) - raise OAuthError('Invalid signature. Expected signature base string: %s' % base) - built = signature_method.build_signature(oauth_request, consumer, token) - - def _check_timestamp(self, timestamp): - # verify that timestamp is recentish - timestamp = int(timestamp) - now = int(time.time()) - lapsed = now - timestamp - if lapsed > self.timestamp_threshold: - raise OAuthError('Expired timestamp: given %d and now %s has a greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) - - def _check_nonce(self, consumer, token, nonce): - # verify that the nonce is uniqueish - nonce = self.data_store.lookup_nonce(consumer, token, nonce) - if nonce: - raise OAuthError('Nonce already used: %s' % str(nonce)) - -# OAuthClient is a worker to attempt to execute a request -class OAuthClient(object): - consumer = None - token = None - - def __init__(self, oauth_consumer, oauth_token): - self.consumer = oauth_consumer - self.token = oauth_token - - def get_consumer(self): - return self.consumer - - def get_token(self): - return self.token - - def fetch_request_token(self, oauth_request): - # -> OAuthToken - raise NotImplementedError - - def fetch_access_token(self, oauth_request): - # -> OAuthToken - raise NotImplementedError - - def access_resource(self, oauth_request): - # -> some protected resource - raise NotImplementedError - -# OAuthDataStore is a database abstraction used to lookup consumers and tokens -class OAuthDataStore(object): - - def lookup_consumer(self, key): - # -> OAuthConsumer - raise NotImplementedError - - def lookup_token(self, oauth_consumer, token_type, token_token): - # -> OAuthToken - raise NotImplementedError - - def lookup_nonce(self, oauth_consumer, oauth_token, nonce, timestamp): - # -> OAuthToken - raise NotImplementedError - - def fetch_request_token(self, oauth_consumer): - # -> OAuthToken - raise NotImplementedError - - def fetch_access_token(self, oauth_consumer, oauth_token): - # -> OAuthToken - raise NotImplementedError - - def authorize_request_token(self, oauth_token, user): - # -> OAuthToken - raise NotImplementedError - -# OAuthSignatureMethod is a strategy class that implements a signature method -class OAuthSignatureMethod(object): - def get_name(self): - # -> str - raise NotImplementedError - - def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): - # -> str key, str raw - raise NotImplementedError - - def build_signature(self, oauth_request, oauth_consumer, oauth_token): - # -> str - raise NotImplementedError - - def check_signature(self, oauth_request, consumer, token, signature): - built = self.build_signature(oauth_request, consumer, token) - return built == signature - -class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): - - def get_name(self): - return 'HMAC-SHA1' - - def build_signature_base_string(self, oauth_request, consumer, token): - sig = ( - escape(oauth_request.get_normalized_http_method()), - escape(oauth_request.get_normalized_http_url()), - escape(oauth_request.get_normalized_parameters()), - ) - - key = '%s&' % escape(consumer.secret) - if token: - key += escape(token.secret) - raw = '&'.join(sig) - return key, raw - - def build_signature(self, oauth_request, consumer, token): - # build the base signature string - key, raw = self.build_signature_base_string(oauth_request, consumer, token) - - # hmac object - try: - import hashlib # 2.5 - hashed = hmac.new(key, raw, hashlib.sha1) - except: - import sha # deprecated - hashed = hmac.new(key, raw, sha) - - # calculate the digest base 64 - return base64.b64encode(hashed.digest()) - -class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): - - def get_name(self): - return 'PLAINTEXT' - - def build_signature_base_string(self, oauth_request, consumer, token): - # concatenate the consumer key and secret - sig = escape(consumer.secret) + '&' - if token: - sig = sig + escape(token.secret) - return sig - - def build_signature(self, oauth_request, consumer, token): - return self.build_signature_base_string(oauth_request, consumer, token) http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/ops.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/ops.py b/wave/src/main/java/python/api/ops.py deleted file mode 100644 index 862c4b5..0000000 --- a/wave/src/main/java/python/api/ops.py +++ /dev/null @@ -1,482 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Support for operations that can be applied to the server. - -Contains classes and utilities for creating operations that are to be -applied on the server. -""" - -import errors -import random -import util -import sys - - -PROTOCOL_VERSION = '0.22' - -# Operation Types -WAVELET_APPEND_BLIP = 'wavelet.appendBlip' -WAVELET_SET_TITLE = 'wavelet.setTitle' -WAVELET_ADD_PARTICIPANT = 'wavelet.participant.add' -WAVELET_DATADOC_SET = 'wavelet.datadoc.set' -WAVELET_MODIFY_TAG = 'wavelet.modifyTag' -WAVELET_MODIFY_PARTICIPANT_ROLE = 'wavelet.modifyParticipantRole' -BLIP_CONTINUE_THREAD = 'blip.continueThread' -BLIP_CREATE_CHILD = 'blip.createChild' -BLIP_DELETE = 'blip.delete' -DOCUMENT_APPEND_MARKUP = 'document.appendMarkup' -DOCUMENT_INLINE_BLIP_INSERT = 'document.inlineBlip.insert' -DOCUMENT_MODIFY = 'document.modify' -ROBOT_CREATE_WAVELET = 'robot.createWavelet' -ROBOT_FETCH_WAVE = 'robot.fetchWave' -ROBOT_NOTIFY = 'robot.notify' -ROBOT_SEARCH = 'robot.search' - -# Assign always NOTIFY_OP_ID to the notify operation so -# we can easily filter it out later -NOTIFY_OP_ID = '0' - -class Operation(object): - """Represents a generic operation applied on the server. - - This operation class contains data that is filled in depending on the - operation type. - - It can be used directly, but doing so will not result - in local, transient reflection of state on the blips. In other words, - creating a 'delete blip' operation will not remove the blip from the local - context for the duration of this session. It is better to use the OpBased - model classes directly instead. - """ - - def __init__(self, method, opid, params): - """Initializes this operation with contextual data. - - Args: - method: Method to call or type of operation. - opid: The id of the operation. Any callbacks will refer to these. - params: An operation type dependent dictionary - """ - self.method = method - self.id = opid - self.params = params - - def __str__(self): - return '%s[%s]%s' % (self.method, self.id, str(self.params)) - - def set_param(self, param, value): - self.params[param] = value - return self - - def serialize(self, method_prefix=''): - """Serialize the operation. - - Args: - method_prefix: prefixed for each method name to allow for specifying - a namespace. - - Returns: - a dict representation of the operation. - """ - if method_prefix and not method_prefix.endswith('.'): - method_prefix += '.' - return {'method': method_prefix + self.method, - 'id': self.id, - 'params': util.serialize(self.params)} - - def set_optional(self, param, value): - """Sets an optional parameter. - - If value is None or "", this is a no op. Otherwise it calls - set_param. - """ - if value == '' or value is None: - return self - else: - return self.set_param(param, value) - - -class OperationQueue(object): - """Wraps the queuing of operations using easily callable functions. - - The operation queue wraps single operations as functions and queues the - resulting operations in-order. Typically there shouldn't be a need to - call this directly unless operations are needed on entities outside - of the scope of the robot. For example, to modify a blip that - does not exist in the current context, you might specify the wave, wavelet - and blip id to generate an operation. - - Any calls to this will not be reflected in the robot in any way. - For example, calling wavelet_append_blip will not result in a new blip - being added to the robot, only an operation to be applied on the - server. - """ - - # Some class global counters: - _next_operation_id = 1 - - def __init__(self, proxy_for_id=None): - self.__pending = [] - self._capability_hash = None - self._proxy_for_id = proxy_for_id - - def _new_blipdata(self, wave_id, wavelet_id, initial_content='', - parent_blip_id=None): - """Creates JSON of the blip used for this session.""" - temp_blip_id = 'TBD_%s_%s' % (wavelet_id, - hex(random.randint(0, sys.maxint))) - return {'waveId': wave_id, - 'waveletId': wavelet_id, - 'blipId': temp_blip_id, - 'content': initial_content, - 'parentBlipId': parent_blip_id} - - def _new_waveletdata(self, domain, participants): - """Creates an ephemeral WaveletData instance used for this session. - - Args: - domain: the domain to create the data for. - participants initially on the wavelet - Returns: - Blipdata (for the rootblip), WaveletData. - """ - wave_id = domain + '!TBD_%s' % hex(random.randint(0, sys.maxint)) - wavelet_id = domain + '!conv+root' - root_blip_data = self._new_blipdata(wave_id, wavelet_id) - participants = set(participants) - wavelet_data = {'waveId': wave_id, - 'waveletId': wavelet_id, - 'rootBlipId': root_blip_data['blipId'], - 'participants': participants} - return root_blip_data, wavelet_data - - def __len__(self): - return len(self.__pending) - - def __iter__(self): - return self.__pending.__iter__() - - def clear(self): - self.__pending = [] - - def proxy_for(self, proxy): - """Return a view of this operation queue with the proxying for set to proxy. - - This method returns a new instance of an operation queue that shares the - operation list, but has a different proxying_for_id set so the robot using - this new queue will send out operations with the proxying_for field set. - """ - res = OperationQueue() - res.__pending = self.__pending - res._capability_hash = self._capability_hash - res._proxy_for_id = proxy - return res - - def set_capability_hash(self, capability_hash): - self._capability_hash = capability_hash - - def serialize(self, method_prefix=''): - first = Operation(ROBOT_NOTIFY, - NOTIFY_OP_ID, - {'capabilitiesHash': self._capability_hash, - 'protocolVersion': PROTOCOL_VERSION}) - operations = [first] + self.__pending - return [op.serialize(method_prefix=method_prefix) for op in operations] - res = util.serialize(operations) - return res - - def copy_operations(self, other_queue): - """Copy the pending operations from other_queue into this one.""" - for op in other_queue: - self.__pending.append(op) - - def new_operation(self, method, wave_id, wavelet_id, props=None, **kwprops): - """Creates and adds a new operation to the operation list.""" - if props is None: - props = {} - props.update(kwprops) - if wave_id is not None: - props['waveId'] = wave_id - if wavelet_id is not None: - props['waveletId'] = wavelet_id - if self._proxy_for_id: - props['proxyingFor'] = self._proxy_for_id - operation = Operation(method, - 'op%s' % OperationQueue._next_operation_id, - props) - self.__pending.append(operation) - OperationQueue._next_operation_id += 1 - return operation - - def wavelet_append_blip(self, wave_id, wavelet_id, initial_content=''): - """Appends a blip to a wavelet. - - Args: - wave_id: The wave id owning the containing wavelet. - wavelet_id: The wavelet id that this blip should be appended to. - initial_content: optionally the content to start with - - Returns: - JSON representing the information of the new blip. - """ - blip_data = self._new_blipdata(wave_id, wavelet_id, initial_content) - self.new_operation(WAVELET_APPEND_BLIP, wave_id, - wavelet_id, blipData=blip_data) - return blip_data - - def wavelet_add_participant(self, wave_id, wavelet_id, participant_id): - """Adds a participant to a wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - participant_id: Id of the participant to add. - - Returns: - data for the root_blip, wavelet - """ - return self.new_operation(WAVELET_ADD_PARTICIPANT, wave_id, wavelet_id, - participantId=participant_id) - - def wavelet_datadoc_set(self, wave_id, wavelet_id, name, data): - """Sets a key/value pair on the data document of a wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - name: The key name for this data. - data: The value of the data to set. - Returns: - The operation created. - """ - return self.new_operation(WAVELET_DATADOC_SET, wave_id, wavelet_id, - datadocName=name, datadocValue=data) - - def robot_create_wavelet(self, domain, participants=None, message=''): - """Creates a new wavelet. - - Args: - domain: the domain to create the wave in - participants: initial participants on this wavelet or None if none - message: an optional payload that is returned with the corresponding - event. - - Returns: - data for the root_blip, wavelet - """ - if participants is None: - participants = [] - blip_data, wavelet_data = self._new_waveletdata(domain, participants) - op = self.new_operation(ROBOT_CREATE_WAVELET, - wave_id=wavelet_data['waveId'], - wavelet_id=wavelet_data['waveletId'], - waveletData=wavelet_data) - op.set_optional('message', message) - return blip_data, wavelet_data - - def robot_search(self, query, index=None, num_results=None): - """Execute a search request. - - For now this only makes sense in the data API. Wave does not maintain - an index for robots so no results will be returned in that scenario. - - Args: - query: what to search for - index: what index to search from - num_results: how many results to return - Returns: - The operation created. - """ - op = self.new_operation( - ROBOT_SEARCH, wave_id=None, wavelet_id=None, query=query) - if index is not None: - op.set_param('index', index) - if num_results is not None: - op.set_param('numResults', num_results) - return op - - def robot_fetch_wave(self, wave_id, wavelet_id, - raw_deltas_from_version=-1, return_raw_snapshot=False): - """Requests a snapshot of the specified wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - raw_deltas_from_version: If specified, return a raw dump of the - delta history of this wavelet, starting at the given version. - This may return only part of the history; use additional - requests with higher raw_deltas_from_version parameters to - get the rest. - return_raw_snapshot: if true, return the raw data for this - wavelet. - Returns: - The operation created. - """ - op = self.new_operation(ROBOT_FETCH_WAVE, wave_id, wavelet_id) - if raw_deltas_from_version != -1: - op.set_param('rawDeltasFromVersion', raw_deltas_from_version) - if return_raw_snapshot: - op.set_param('returnRawSnapshot', return_raw_snapshot) - return op - - def wavelet_set_title(self, wave_id, wavelet_id, title): - """Sets the title of a wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - title: The title to set. - Returns: - The operation created. - """ - return self.new_operation(WAVELET_SET_TITLE, wave_id, wavelet_id, - waveletTitle=title) - - def wavelet_modify_participant_role( - self, wave_id, wavelet_id, participant_id, role): - """Modify the role of a participant on a wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - participant_id: Id of the participant to add. - role: the new roles - - Returns: - data for the root_blip, wavelet - """ - return self.new_operation(WAVELET_MODIFY_PARTICIPANT_ROLE, wave_id, - wavelet_id, participantId=participant_id, - participantRole=role) - - def wavelet_modify_tag(self, wave_id, wavelet_id, tag, modify_how=None): - """Modifies a tag in a wavelet. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - tag: The tag (a string). - modify_how: (optional) how to apply the tag. The default is to add - the tag. Specify 'remove' to remove. Specify None or 'add' to - add. - Returns: - The operation created. - """ - return self.new_operation(WAVELET_MODIFY_TAG, wave_id, wavelet_id, - name=tag).set_optional("modify_how", modify_how) - - def blip_create_child(self, wave_id, wavelet_id, blip_id): - """Creates a child blip of another blip. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - - Returns: - JSON of blip for which further operations can be applied. - """ - blip_data = self._new_blipdata(wave_id, wavelet_id, parent_blip_id=blip_id) - self.new_operation(BLIP_CREATE_CHILD, wave_id, wavelet_id, - blipId=blip_id, - blipData=blip_data) - return blip_data - - def blip_continue_thread(self, wave_id, wavelet_id, blip_id): - """Creates a blip in same thread as specified blip. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - - Returns: - JSON of blip for which further operations can be applied. - """ - blip_data = self._new_blipdata(wave_id, wavelet_id) - self.new_operation(BLIP_CONTINUE_THREAD, wave_id, wavelet_id, - blipId=blip_id, - blipData=blip_data) - return blip_data - - - def blip_delete(self, wave_id, wavelet_id, blip_id): - """Deletes the specified blip. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - Returns: - The operation created. - """ - return self.new_operation(BLIP_DELETE, wave_id, wavelet_id, blipId=blip_id) - - def document_append_markup(self, wave_id, wavelet_id, blip_id, content): - """Appends content with markup to a document. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - content: The markup content to append. - Returns: - The operation created. - """ - return self.new_operation(DOCUMENT_APPEND_MARKUP, wave_id, wavelet_id, - blipId=blip_id, content=content) - - def document_modify(self, wave_id, wavelet_id, blip_id): - """Creates and queues a document modify operation - - The returned operation still needs to be filled with details before - it makes sense. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - Returns: - The operation created. - """ - return self.new_operation(DOCUMENT_MODIFY, - wave_id, - wavelet_id, - blipId=blip_id) - - def document_inline_blip_insert(self, wave_id, wavelet_id, blip_id, position): - """Inserts an inline blip at a specific location. - - Args: - wave_id: The wave id owning that this operation is applied to. - wavelet_id: The wavelet id that this operation is applied to. - blip_id: The blip id that this operation is applied to. - position: The position in the document to insert the blip. - - Returns: - JSON data for the blip that was created for further operations. - """ - inline_blip_data = self._new_blipdata(wave_id, wavelet_id) - inline_blip_data['parentBlipId'] = blip_id - self.new_operation(DOCUMENT_INLINE_BLIP_INSERT, wave_id, wavelet_id, - blipId=blip_id, - index=position, - blipData=inline_blip_data) - return inline_blip_data http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/78cbf78f/wave/src/main/java/python/api/ops_test.py ---------------------------------------------------------------------- diff --git a/wave/src/main/java/python/api/ops_test.py b/wave/src/main/java/python/api/ops_test.py deleted file mode 100644 index 37f1d85..0000000 --- a/wave/src/main/java/python/api/ops_test.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/python -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -"""Unit tests for the ops module.""" - - -import unittest - -import ops - - -class TestOperation(unittest.TestCase): - """Test case for Operation class.""" - - def testFields(self): - op = ops.Operation(ops.WAVELET_SET_TITLE, 'opid02', - {'waveId': 'wavelet-id', - 'title': 'a title'}) - self.assertEqual(ops.WAVELET_SET_TITLE, op.method) - self.assertEqual('opid02', op.id) - self.assertEqual(2, len(op.params)) - - def testConstructModifyTag(self): - q = ops.OperationQueue() - op = q.wavelet_modify_tag('waveid', 'waveletid', 'tag') - self.assertEqual(3, len(op.params)) - op = q.wavelet_modify_tag( - 'waveid', 'waveletid', 'tag', modify_how='remove') - self.assertEqual(4, len(op.params)) - - def testConstructRobotFetchWave(self): - q = ops.OperationQueue('proxyid') - op = q.robot_fetch_wave('wave1', 'wavelet1') - self.assertEqual(3, len(op.params)) - self.assertEqual('proxyid', op.params['proxyingFor']) - self.assertEqual('wave1', op.params['waveId']) - self.assertEqual('wavelet1', op.params['waveletId']) - op = q.robot_fetch_wave('wave1', 'wavelet1', - raw_deltas_from_version=5, return_raw_snapshot=True) - self.assertEqual(5, len(op.params)) - self.assertEqual(5, op.params['rawDeltasFromVersion']) - self.assertEqual(True, op.params['returnRawSnapshot']) - -class TestOperationQueue(unittest.TestCase): - """Test case for OperationQueue class.""" - - def testSerialize(self): - q = ops.OperationQueue() - q.set_capability_hash('hash') - op = q.wavelet_modify_tag('waveid', 'waveletid', 'tag') - json = q.serialize() - self.assertEqual(2, len(json)) - self.assertEqual('robot.notify', json[0]['method']) - self.assertEqual('hash', json[0]['params']['capabilitiesHash']) - self.assertEqual(ops.PROTOCOL_VERSION, json[0]['params']['protocolVersion']) - self.assertEqual('wavelet.modifyTag', json[1]['method']) - -if __name__ == '__main__': - unittest.main()
