Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use BaseResponse for proposal history to add API support #6039

Merged
merged 32 commits into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
02c2022
Remove unused import.
Nov 5, 2019
2e1a69a
No need for same history record uid for submitted document.
Nov 5, 2019
1b367d3
No need for same history record uid when creating submitted proposal.
Nov 5, 2019
31c072c
No need for same history record uid when submitting document.
Nov 5, 2019
0817caa
Remove unused uuid parameter for Proposal.comment method.
Nov 5, 2019
5f097a7
Add ProposalResponse class to replace BaseHistoryRecord.
Nov 5, 2019
05cea34
Use ProposalResponse instead of HistoryRecord.
Nov 5, 2019
458a29b
Add field deserializer for Persistent objects.
Nov 8, 2019
15fc4ec
Add serializer for ProposalResponse objects.
Nov 8, 2019
72ce277
Fix created field type in Response objects.
Nov 8, 2019
600455c
Add methods to serialize and deserialize a ProposalResponse.
Nov 8, 2019
ef09126
Sync proposal responses when necessary.
Nov 8, 2019
5d9628d
Add ProposalResponseDescription to help display correct response mess…
Nov 8, 2019
0e8665f
Move ProposalResponse messages into ProposalResponseDescription.
Nov 8, 2019
e4a999b
Display ProposalResponses in overview instead of ProposalHistory.
Nov 8, 2019
66479fa
Generate activity on proposal when commenting a submitted proposal.
Nov 8, 2019
5dea02e
Clean up unused code in proposalhistory.
Nov 8, 2019
40a1797
Add upgrade step to migrate proposal history to proposal responses.
Nov 8, 2019
f91bc23
Sync proposal responses when submitting a document.
Nov 11, 2019
d96e25b
No need for ticking creation of proposal history in fixture.
Nov 11, 2019
9880264
Remove broken upgrade steps.
Nov 11, 2019
a0e2fc3
Limit created precision to seconds in ProposalResponse.
Nov 13, 2019
ac14536
Adapt proposal history tests to test proposal responses instead.
Nov 11, 2019
6d68b39
Add CL entry.
Nov 11, 2019
8d2f3d6
Add test for proposal GET over REST-API.
Nov 11, 2019
dc7dd29
Add documentation proposals API endpoint.
Nov 12, 2019
83651fd
Also test responses in proposal creation over API.
Nov 12, 2019
28cb733
Make sure to use unicode string as default for ProposalResponse text.
Nov 12, 2019
759318b
Set correct default value for text field in Response.
Nov 13, 2019
6e6e450
Make sure that text is set to empty string in ProposalHistory.
Nov 13, 2019
8946475
Include corrections and comments from PR review.
Nov 18, 2019
4634114
Correct typo in ProposalCommentedActivity class name.
Nov 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/HISTORY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Changelog
- Add main dossier count to contentstats. [njohner]
- No longer add journal entry for document file modification. [njohner]
- Revoke permissions for former responsible, when task gets rejected. [phgross]
- Use BaseResponse for proposal history to add API support. [njohner]


2019.4.0rc5 (2019-11-13)
Expand Down
53 changes: 53 additions & 0 deletions docs/public/dev-manual/api/proposals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,56 @@ Auch Anträge können via REST API bedient werden. Die Erstellung eines Antrags
},
"...": "..."
}


Antragverlauf
-------------
Der Verlauf eines Antrags ist in der GET Repräsentation eines Antrags unter dem Attribut ``responses`` enthalten.


**Beispiel-Respones auf ein GET Request**:

.. sourcecode:: http

HTTP/1.1 200 OK
Accept: application/json

{
"@id": "http://example.org/ordnungssystem/fuehrung/dossier-1/proposal-5",
"@type": "opengever.meeting.proposal",
"UID": "3a551f6e3b62421da029dfceb71656e6",
"items": [],
"responses": [
{
"@id": "http://example.org/ordnungssystem/fuehrung/dossier-1/proposal-5/@responses/1569394746972113",
"response_id": 1569394746972113,
"response_type": "successor_created",
"additional_data": {
"successor_oguid": "fd:593382572"
},
"changes": [],
"creator": {
"title": "hugo.boss",
"token": "hugo.boss"
},
"created": "2019-05-21T13:57:42",
"text": "",
},
{
"@id": "http://example.org/ordnungssystem/fuehrung/dossier-1/proposal-5/@responses/1573486804000000",
"response_id": 1573486804000000
"response_type": "commented"
"additional_data": [],
"changes": [],
"creator": {
"title": "hugo.boss",
"token": "hugo.boss"
},
"created": "2019-11-11T16:40:04",
"text": "Suspendisse faucibus, nunc et pellentesque egestas.",
},
]
"review_state": "proposal-state-submitted",
"...": "...",
}

3 changes: 3 additions & 0 deletions opengever/api/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
<adapter factory=".response.SerializeResponseToJson" />
<adapter factory=".task.SerializeTaskResponseToJson" />
<adapter factory=".task.TaskDeserializeFromJson" />
<adapter factory=".proposal.SerializeProposalResponseToJson" />
<adapter factory=".serializer.long_converter" />
<adapter factory=".field_deserializers.PersistentDefaultFieldDeserializer" />
<adapter factory=".field_deserializers.PersistentDatetimeFieldDeserializer" />

<adapter factory=".todo.DeserializeToDoFromJson" />
<adapter factory=".mail.DeserializeMailFromJson" />
Expand Down
22 changes: 22 additions & 0 deletions opengever/api/field_deserializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from plone.restapi.deserializer.dxfields import DatetimeFieldDeserializer
from plone.restapi.deserializer.dxfields import DefaultFieldDeserializer
from plone.restapi.interfaces import IFieldDeserializer
from persistent.interfaces import IPersistent
from zope.component import adapter
from zope.interface import implementer
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.schema.interfaces import IDatetime
from zope.schema.interfaces import IField


@implementer(IFieldDeserializer)
@adapter(IField, IPersistent, IBrowserRequest)
class PersistentDefaultFieldDeserializer(DefaultFieldDeserializer):
"""Default field deserializer for persisten objects"""


@implementer(IFieldDeserializer)
@adapter(IDatetime, IPersistent, IBrowserRequest)
class PersistentDatetimeFieldDeserializer(DatetimeFieldDeserializer):
"""Datetime field deserializer for persisten objects"""
13 changes: 13 additions & 0 deletions opengever/api/proposal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from opengever.api.response import SerializeResponseToJson
from plone.restapi.interfaces import ISerializeToJson
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface
from opengever.meeting.proposalhistory import IProposalResponse


@implementer(ISerializeToJson)
@adapter(IProposalResponse, Interface)
class SerializeProposalResponseToJson(SerializeResponseToJson):

model = IProposalResponse
6 changes: 6 additions & 0 deletions opengever/api/tests/test_content_creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from opengever.api.add import get_validation_errors
from opengever.base.behaviors.lifecycle import ILifeCycle
from opengever.base.oguid import Oguid
from opengever.base.response import IResponseContainer
from opengever.dossier.behaviors.dossier import IDossier
from opengever.repository.interfaces import IRepositoryFolder
from opengever.testing import FunctionalTestCase
Expand Down Expand Up @@ -254,6 +255,11 @@ def test_proposal_creation(self, browser):
self.assertEqual(IBumblebeeDocument(self.proposal_template).get_checksum(),
checksum)

# Creating a proposal should generate a history entry
responses = IResponseContainer(proposal).list()
self.assertEqual(1, len(responses))
self.assertEqual('created', responses[0].response_type)

@browsing
def test_template_is_mandatory_for_proposal_creation(self, browser):
self.login(self.meeting_user, browser)
Expand Down
75 changes: 75 additions & 0 deletions opengever/api/tests/test_proposal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from ftw.testbrowser import browsing
from opengever.testing import IntegrationTestCase
from os.path import join as pjoin
from plone.restapi.serializer.converters import json_compatible


class TestProposalSerialization(IntegrationTestCase):

@browsing
def test_proposal_contains_a_list_of_responses(self, browser):
self.login(self.regular_user, browser=browser)
browser.open(self.proposal, method="GET", headers=self.api_headers)
self.maxDiff = None
responses = browser.json['responses']

self.assertEquals(3, len(responses))

response_id = 1472645373000000
expected_path = pjoin(self.proposal.absolute_url(),
'@responses',
str(response_id))
self.assertEquals(
{u'@id': expected_path,
u'additional_data': {},
u'changes': [],
u'created': json_compatible(self.proposal.created().utcdatetime()),
u'creator': {u'title': u'Ziegler Robert', u'token': u'robert.ziegler'},
u'response_id': response_id,
u'response_type': u'created',
u'text': u''},
responses[0])

@browsing
def test_getting_specific_response_from_proposal(self, browser):
self.login(self.regular_user, browser=browser)

response_id = 1472645373000000
url = pjoin(self.proposal.absolute_url(), '@responses', str(response_id))
browser.open(url, method="GET", headers=self.api_headers)
self.maxDiff = None

self.assertEquals(
{u'@id': url,
u'additional_data': {},
u'changes': [],
u'created': json_compatible(self.proposal.created().utcdatetime()),
u'creator': {u'title': u'Ziegler Robert', u'token': u'robert.ziegler'},
u'response_id': response_id,
u'response_type': u'created',
u'text': u''},
browser.json)

@browsing
def test_submitted_proposal_contains_a_list_of_responses(self, browser):
self.login(self.meeting_user, browser=browser)
browser.open(self.submitted_proposal, method="GET", headers=self.api_headers)
self.maxDiff = None
responses = browser.json['responses']

self.assertEquals(2, len(responses))

response_id = 1472645373000000
expected_path = pjoin(self.submitted_proposal.absolute_url(),
'@responses',
str(response_id))
self.assertEquals(
{u'@id': expected_path,
u'additional_data': {},
u'changes': [],
u'created': json_compatible(self.submitted_proposal.created().utcdatetime()),
u'creator': {u'title': u'Ziegler Robert', u'token': u'robert.ziegler'},
u'response_id': response_id,
u'response_type': u'submitted',
u'text': u''},
responses[0])
7 changes: 3 additions & 4 deletions opengever/base/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from zope.interface import implements
from zope.interface import Interface
from zope.schema import getFields
import time


class IResponseAddedEvent(IObjectEvent):
Expand Down Expand Up @@ -145,11 +144,11 @@ class IResponse(Interface):

response_id = schema.Int(required=True)

created = schema.Date(required=True)
created = schema.Datetime(required=True)

creator = schema.TextLine(required=True)

text = schema.Text(required=False)
text = schema.Text(required=False, default=u'', missing_value=u'')

changes = schema.List(required=False, value_type=schema.Dict())

Expand All @@ -169,7 +168,7 @@ def __init__(self, response_type='default'):
self.created = datetime.now()
self.creator = api.user.get_current().id

self.text = ''
self.text = u''
self.changes = PersistentList()

def add_change(self, field_id, before, after, field_title=''):
Expand Down
Loading