~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

# pylint: disable-msg=E0211,E0213

"""Launchpad Pillars share a namespace.

Pillars are currently Product, ProjectGroup and Distribution.
"""

__metaclass__ = type

from lazr.restful.declarations import (
    export_as_webservice_entry,
    export_read_operation,
    exported,
    operation_parameters,
    operation_returns_collection_of,
    )
from lazr.restful.fields import (
    CollectionField,
    Reference,
    )
from zope.interface import (
    Attribute,
    Interface,
    )
from zope.schema import (
    Bool,
    Int,
    List,
    TextLine,
    )

from canonical.launchpad import _


__all__ = ['IHasAliases', 'IPillar', 'IPillarName', 'IPillarNameSet']


class IPillar(Interface):
    """An object that might be a project, a project group, or a distribution.

    This is a polymorphic object served by the pillar set. Check the
    individual object to see what type it is.
    """
    export_as_webservice_entry()
    active = exported(
        Bool(title=_('Active'),
             description=_("Whether or not this item is active.")))


class IHasAliases(Interface):

    aliases = List(
        title=_('Aliases'), required=False, readonly=True,
        description=_(
            "The names (as strings) which are aliases to this pillar."))

    # Instead of a method for setting aliases we could make the 'aliases'
    # attribute writable, but we decided to go with a method because this
    # operation may trigger several inserts/deletes in the database and a
    # method helps clarifying it may be an expensive operation.
    def setAliases(names):
        """Set the given names as this pillar's aliases.

        For each of the given names, check that it's not already in use by
        another pillar and then make sure it exists as an alias for this
        pillar.  If the given names don't include any of this pillar's
        existing aliases, these are deleted.

        :param names: A sequence of names (as strings) that should be aliases
            to this pillar.
        """


class IPillarName(Interface):
    """A data structure for identifying a pillar.

    This includes the pillar object, as well as information about whether
    it's a project, project group, or distribution.
    """
    id = Int(title=_('The PillarName ID'))
    name = TextLine(title=u"The name.")
    product = Attribute('The project that has this name, or None')
    project = Attribute('The project that has this name, or None')
    distribution = Attribute('The distribution that has this name, or None')
    active = Attribute('The pillar is active')
    pillar = Attribute('The pillar object')


class IPillarNameSet(Interface):
    """An object for searching across projects, project groups, and distros.

    Projects, project groups, and distributions are collectively known as
    "pillars". This object lets you do a combined search across all
    types of pillars. It also gives you access to pillars that have
    been flagged by administrators as "featured" pillars.
    """
    export_as_webservice_entry('pillars')

    def __contains__(name):
        """True if the given name is an active Pillar or an alias to one."""

    def __getitem__(name):
        """Get an active pillar by its name or any of its aliases.

        If there's no pillar with the given name or there is one but it's
        inactive, raise NotFoundError.
        """

    def getByName(name, ignore_inactive=False):
        """Return the pillar whose name or alias matches the given name.

        If ignore_inactive is True, then only active pillars are considered.

        If no pillar is found, return None.
        """

    def count_search_matches(text):
        """Return the total number of Pillars matching :text:"""


    @operation_parameters(text=TextLine(title=u"Search text"),
                          limit=Int(title=u"Maximum number of items to "
                                    "return. This is a hard limit: any "
                                    "pagination you request will happen "
                                    "within this limit.",
                                    required=False))
    @operation_returns_collection_of(IPillar)
    @export_read_operation()
    def search(text, limit):
        """Return Projects/Project groups/Distros matching :text:.

        If :limit: is None, the default batch size will be used.

        The results are ordered descending by rank.
        """

    def add_featured_project(project):
        """Add a project to the featured project list."""

    def remove_featured_project(project):
        """Remove a project from the featured project list."""

    featured_projects = exported(
        CollectionField(
            title=_('Projects, project groups, and distributions that are '
                    'featured on the site.'),
            value_type=Reference(schema=IPillar)),
        exported_as="featured_pillars"
        )