← Back to team overview

launchpad-dev team mailing list archive

Re: How do I support older API calls when changing the underlying model?

 

> I'm in the situation where I'm wanting to split up a status field on the branch 
> merge proposal into two.  However there is an existing API that is used to 
> fetch merge proposals.

OK, let me quote the part of the system I think you're changing.

From IHasMergeProposals:

    @operation_parameters(
        status=List(
            title=_("A list of merge proposal statuses to filter by."),
            value_type=Choice(vocabulary=BranchMergeProposalStatus)))
    @call_with(visible_by_user=REQUEST_USER)
    @operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
    @export_read_operation()
    def getMergeProposals(status=None, visible_by_user=None):

From IBranchMergeProposal:

    queue_status = exported(
        Choice(
            title=_('Status'),
            vocabulary=BranchMergeProposalStatus, required=True,
            readonly=True,
            description=_("The current state of the proposal.")))

> How do we continue to support the older methods when changing the underlying 
> model?  Should we even try?

Sometimes it's possible to determine that no one is using the old
version of a named operation, so you can change it without pain, or that
only a couple people are using it and you can coordinate a change with
them.

However, this change doesn't look too difficult, so let's try
maintaining the backwards compatibility.

> I'm wanting to break the queue_status member into merge_status and 
> review_status.  I was also considering "re-using" the 
> BranchMergeProposalStatus enum for the merge_status with changed values.
> 
> However I feel that this is more likely to break something.
> 
> I'm at a bit of a loss to know where to start with supporting the old method 
> where the method directly gets values from an enumerated type I'm changing.
> 
> Should I instead of reusing the BranchMergeProposalStatus enum, create two new 
> ones (instead of just one new one)?

You should. All of the versions of the web service coexist in a single
application. Launchpad now has three enumerated types: one old one and
two new ones. They all need to be present.

> How do I have a method that is only supported in an old API version?

With the @operation_removed_in() decorator. Here's the old method,
renamed to getMergeProposalsOLD internally, published as
getMergeProposals externally, and removed in the latest version of the
web service:

    @operation_removed_in("devel")
    @operation_parameters(
        status=List(
            title=_("A list of merge proposal statuses to filter by."),
            value_type=Choice(vocabulary=BranchMergeProposalStatus)))
    @call_with(visible_by_user=REQUEST_USER)
    @operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
    @export_as("getMergeProposals")
    @export_read_operation()
    def getMergeProposalsOLD(status=None, visible_by_user=None):

Here's the new method, introduced in the latest version of the web
service.

    @operation_parameters(
       merge_status=List(
           value_type=Choice(
               Vocabulary=BranchMergeProposalMergeStatus)))),
       review_status=List(
           value_type=Choice(
               Vocabulary=BranchMergeProposalReviewStatus)))
    )
    @call_with(visible_by_user=REQUEST_USER)
    @operation_returns_collection_of(Interface) # Really
IBranchMergeProposal.
    @export_read_operation()
    @operation_for_version("devel")
    def getMergeProposals(merge_status=None, review_status=None,
visible_by_user=None):

This way the 'getMergeProposals' named operation will have the existing
behavior in 'beta' and '1.0' (because it's mapped to
getMergeProposalsOLD()), and new behavior in subsequent versions
(because it's mapped to getMergeProposals()).

And here's how to get rid of queue_status in the latest version while
maintaining it in the old versions.

    queue_status = exported(
        Choice(
            title=_('Status'),
            vocabulary=BranchMergeProposalStatus, required=True,
            readonly=True,
            description=_("The current state of the proposal.")),
        ('devel', dict(exported=False)))


Leonard

Attachment: signature.asc
Description: This is a digitally signed message part


Follow ups

References