Copybara | 854996b | 2021-09-07 19:36:02 +0000 | [diff] [blame] | 1 | # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style |
| 3 | # license that can be found in the LICENSE file or at |
| 4 | # https://developers.google.com/open-source/licenses/bsd |
| 5 | |
| 6 | """Protocol buffers for Monorail projects.""" |
| 7 | |
| 8 | from __future__ import print_function |
| 9 | from __future__ import division |
| 10 | from __future__ import absolute_import |
| 11 | |
| 12 | from protorpc import messages |
| 13 | |
| 14 | # Project state affects permissions in that project, and project deletion. |
| 15 | # It is edited on the project admin page. If it is anything other that LIVE |
| 16 | # it triggers a notice at the top of every project page. |
| 17 | # For more info, see the "Project deletion in Monorail" design doc. |
| 18 | class ProjectState(messages.Enum): |
| 19 | """Enum for states in the project lifecycle.""" |
| 20 | # Project is visible and indexed. This is the typical state. |
| 21 | # |
| 22 | # If moved_to is set, this project is live but has been moved |
| 23 | # to another location, so redirects will be used or links shown. |
| 24 | LIVE = 1 |
| 25 | |
| 26 | # Project owner has requested the project be archived. Project is |
| 27 | # read-only to members only, off-limits to non-members. Issues |
| 28 | # can be searched when in the project, but should not appear in |
| 29 | # site-wide searches. The project name is still in-use by this |
| 30 | # project. |
| 31 | # |
| 32 | # If a delete_time is set, then the project is doomed: (1) the |
| 33 | # state can only be changed by a site admin, and (2) the project |
| 34 | # will automatically transition to DELETABLE after that time is |
| 35 | # reached. |
| 36 | ARCHIVED = 2 |
| 37 | |
| 38 | # Project can be deleted at any time. The project name should |
| 39 | # have already been changed to a generated string, so it's |
| 40 | # impossible to navigate to this project, and the original name |
| 41 | # can be reused by a new project. |
| 42 | DELETABLE = 3 |
| 43 | |
| 44 | |
| 45 | # Project access affects permissions in that project. |
| 46 | # It is edited on the project admin page. |
| 47 | class ProjectAccess(messages.Enum): |
| 48 | """Enum for possible project access levels.""" |
| 49 | # Anyone may view this project, even anonymous users. |
| 50 | ANYONE = 1 |
| 51 | |
| 52 | # Only project members may view the project. |
| 53 | MEMBERS_ONLY = 3 |
| 54 | |
| 55 | |
| 56 | # A Project PB represents a project in Monorail, which is a workspace for |
| 57 | # project members to collaborate on issues. |
| 58 | # A project is created on the project creation page, searched on the project |
| 59 | # list page, and edited on the project admin page. |
| 60 | # Next message: 74 |
| 61 | class Project(messages.Message): |
| 62 | """This protocol buffer holds all the metadata associated with a project.""" |
| 63 | state = messages.EnumField(ProjectState, 1, required=True) |
| 64 | access = messages.EnumField(ProjectAccess, 18, default=ProjectAccess.ANYONE) |
| 65 | |
| 66 | # The short identifier for this project. This value is lower-cased, |
| 67 | # and must be between 3 and 20 characters (inclusive). Alphanumeric |
| 68 | # and dashes are allowed, and it must start with an alpha character. |
| 69 | # Project names must be unique. |
| 70 | project_name = messages.StringField(2, required=True) |
| 71 | |
| 72 | # A numeric identifier for this project. |
| 73 | project_id = messages.IntegerField(3, required=True) |
| 74 | |
| 75 | # A one-line summary (human-readable) name of the project. |
| 76 | summary = messages.StringField(4, default='') |
| 77 | |
| 78 | # A detailed description of the project. |
| 79 | description = messages.StringField(5, default='') |
| 80 | |
| 81 | # Description of why this project has the state set as it is. |
| 82 | # This is used for administrative purposes to notify Owners that we |
| 83 | # are going to delete their project unless they can provide a good |
| 84 | # reason to not do so. |
| 85 | state_reason = messages.StringField(9) |
| 86 | |
| 87 | # Time (in seconds) at which an ARCHIVED project may automatically |
| 88 | # be changed to state DELETABLE. The state change is done by a |
| 89 | # cron job. |
| 90 | delete_time = messages.IntegerField(10) |
| 91 | |
| 92 | # Note that these lists are disjoint (a user ID will not appear twice). |
| 93 | owner_ids = messages.IntegerField(11, repeated=True) |
| 94 | committer_ids = messages.IntegerField(12, repeated=True) |
| 95 | contributor_ids = messages.IntegerField(15, repeated=True) |
| 96 | |
| 97 | class ExtraPerms(messages.Message): |
| 98 | """Nested message for each member's extra permissions in a project.""" |
| 99 | member_id = messages.IntegerField(1, required=True) |
| 100 | # Each custom perm is a single word [a-zA-Z0-9]. |
| 101 | perms = messages.StringField(2, repeated=True) |
| 102 | |
| 103 | extra_perms = messages.MessageField(ExtraPerms, 16, repeated=True) |
| 104 | |
| 105 | # Project owners may choose to have ALL issue change notifications go to a |
| 106 | # mailing list (in addition to going directly to the users interested |
| 107 | # in that issue). |
| 108 | issue_notify_address = messages.StringField(14) |
| 109 | |
| 110 | # These fields keep track of the cumulative size of all issue attachments |
| 111 | # in a given project. Normally, the number of bytes used is compared |
| 112 | # to a constant defined in the web application. However, if a custom |
| 113 | # quota is specified here, it will be used instead. An issue attachment |
| 114 | # will fail if its size would put the project over its quota. Not all |
| 115 | # projects have these fields: they are only set when the first attachment |
| 116 | # is uploaded. |
| 117 | attachment_bytes_used = messages.IntegerField(38, default=0) |
| 118 | # If quota is not set, default from tracker_constants.py is used. |
| 119 | attachment_quota = messages.IntegerField(39) |
| 120 | |
| 121 | # NOTE: open slots 40, 41 |
| 122 | |
| 123 | # Recent_activity is a timestamp (in seconds since the Epoch) of the |
| 124 | # last time that an issue was entered, updated, or commented on. |
| 125 | recent_activity = messages.IntegerField(42, default=0) |
| 126 | |
| 127 | # NOTE: open slots 43... |
| 128 | |
| 129 | # Timestamp (in seconds since the Epoch) of the most recent change |
| 130 | # to this project that would invalidate cached content. It is set |
| 131 | # whenever project membership is edited, or any component config PB |
| 132 | # is edited. HTTP requests for auto-complete feeds include this |
| 133 | # value in the URL. |
| 134 | cached_content_timestamp = messages.IntegerField(53, default=0) |
| 135 | |
| 136 | # If set, this project has been moved elsewhere. This can |
| 137 | # be an absolute URL, the name of another project on the same site. |
| 138 | moved_to = messages.StringField(60) |
| 139 | |
| 140 | # Enable inbound email processing for issues. |
| 141 | process_inbound_email = messages.BooleanField(63, default=False) |
| 142 | |
| 143 | # Limit removal of Restrict-* labels to project owners. |
| 144 | only_owners_remove_restrictions = messages.BooleanField(64, default=False) |
| 145 | |
| 146 | # A per-project read-only lock. This lock (1) is meant to be |
| 147 | # long-lived (lasting as long as migration operations, project |
| 148 | # deletion, or anything else might take and (2) is meant to only |
| 149 | # limit user mutations; whether or not it limits automated actions |
| 150 | # that would change project data (such as workflow items) is |
| 151 | # determined based on the action. |
| 152 | # |
| 153 | # This lock is implemented as a user-visible string describing the |
| 154 | # reason for the project being in a read-only state. An absent or empty |
| 155 | # value indicates that the project is read-write; a present and |
| 156 | # non-empty value indicates that the project is read-only for the |
| 157 | # reason described. |
| 158 | read_only_reason = messages.StringField(65) |
| 159 | |
| 160 | # This option is rarely used, but it makes sense for projects that aim for |
| 161 | # hub-and-spoke collaboration bewtween a vendor organization (like Google) |
| 162 | # and representatives of partner companies who are not supposed to know |
| 163 | # about each other. |
| 164 | # When true, it prevents project committers, contributors, and visitors |
| 165 | # from seeing the list of project members on the project summary page, |
| 166 | # on the People list page, and in autocomplete for issue owner and Cc. |
| 167 | # Project owners can always see the complete list of project members. |
| 168 | only_owners_see_contributors = messages.BooleanField(66, default=False) |
| 169 | |
| 170 | # This configures the URLs generated when autolinking revision numbers. |
| 171 | # E.g., gitiles, viewvc, or crrev.com. |
| 172 | revision_url_format = messages.StringField(67) |
| 173 | |
| 174 | # The home page of the Project. |
| 175 | home_page = messages.StringField(68) |
| 176 | # The url to redirect to for wiki/documentation links. |
| 177 | docs_url = messages.StringField(71) |
| 178 | # The url to redirect to for wiki/documentation links. |
| 179 | source_url = messages.StringField(72) |
| 180 | # The GCS object ID of the Project's logo. |
| 181 | logo_gcs_id = messages.StringField(69) |
| 182 | # The uploaded file name of the Project's logo. |
| 183 | logo_file_name = messages.StringField(70) |
| 184 | |
| 185 | # Always send the full content of update in notifications. |
| 186 | issue_notify_always_detailed = messages.BooleanField(73, default=False) |
| 187 | |
| 188 | |
| 189 | # This PB documents some of the duties of some of the members |
| 190 | # in a given project. This info is displayed on the project People page. |
| 191 | class ProjectCommitments(messages.Message): |
| 192 | project_id = messages.IntegerField(50) |
| 193 | |
| 194 | class MemberCommitment(messages.Message): |
| 195 | member_id = messages.IntegerField(11, required=True) |
| 196 | notes = messages.StringField(13) |
| 197 | |
| 198 | commitments = messages.MessageField(MemberCommitment, 2, repeated=True) |
| 199 | |
| 200 | |
| 201 | def MakeProject( |
| 202 | project_name, project_id=None, state=ProjectState.LIVE, |
| 203 | access=ProjectAccess.ANYONE, summary=None, description=None, |
| 204 | moved_to=None, cached_content_timestamp=None, |
| 205 | owner_ids=None, committer_ids=None, contributor_ids=None, |
| 206 | read_only_reason=None, home_page=None, docs_url=None, source_url=None, |
| 207 | logo_gcs_id=None, logo_file_name=None): |
| 208 | """Returns a project protocol buffer with the given attributes.""" |
| 209 | project = Project( |
| 210 | project_name=project_name, access=access, state=state) |
| 211 | if project_id: |
| 212 | project.project_id = project_id |
| 213 | if moved_to: |
| 214 | project.moved_to = moved_to |
| 215 | if cached_content_timestamp: |
| 216 | project.cached_content_timestamp = cached_content_timestamp |
| 217 | if summary: |
| 218 | project.summary = summary |
| 219 | if description: |
| 220 | project.description = description |
| 221 | if home_page: |
| 222 | project.home_page = home_page |
| 223 | if docs_url: |
| 224 | project.docs_url = docs_url |
| 225 | if source_url: |
| 226 | project.source_url = source_url |
| 227 | if logo_gcs_id: |
| 228 | project.logo_gcs_id = logo_gcs_id |
| 229 | if logo_file_name: |
| 230 | project.logo_file_name = logo_file_name |
| 231 | |
| 232 | project.owner_ids.extend(owner_ids or []) |
| 233 | project.committer_ids.extend(committer_ids or []) |
| 234 | project.contributor_ids.extend(contributor_ids or []) |
| 235 | |
| 236 | if read_only_reason is not None: |
| 237 | project.read_only_reason = read_only_reason |
| 238 | |
| 239 | return project |