summaryrefslogtreecommitdiffstats
path: root/java/com/google/gerrit/server/ChangeMessagesUtil.java
blob: 81cff6e039b356ef64559fb7fd4080c434d0c056 (plain)
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed 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.

package com.google.gerrit.server;

import static com.google.common.collect.ImmutableSet.toImmutableSet;

import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.ChangeMessage;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.util.AccountTemplateUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;

/** Utility functions to manipulate {@link ChangeMessage}. */
@Singleton
public class ChangeMessagesUtil {
  public static final String AUTOGENERATED_TAG_PREFIX = "autogenerated:";
  public static final String AUTOGENERATED_BY_GERRIT_TAG_PREFIX =
      AUTOGENERATED_TAG_PREFIX + "gerrit:";

  public static final String TAG_ABANDON = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "abandon";
  public static final String TAG_CHERRY_PICK_CHANGE =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "cherryPickChange";
  public static final String TAG_DELETE_ASSIGNEE =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "deleteAssignee";
  public static final String TAG_DELETE_REVIEWER =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "deleteReviewer";
  public static final String TAG_DELETE_VOTE = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "deleteVote";
  public static final String TAG_MERGED = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "merged";
  public static final String TAG_MOVE = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "move";
  public static final String TAG_RESTORE = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "restore";
  public static final String TAG_REVERT = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "revert";
  public static final String TAG_SET_ASSIGNEE = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setAssignee";
  public static final String TAG_UPDATE_ATTENTION_SET =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "updateAttentionSet";
  public static final String TAG_SET_DESCRIPTION =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setPsDescription";
  public static final String TAG_SET_HASHTAGS = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setHashtag";
  public static final String TAG_SET_PRIVATE = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setPrivate";
  public static final String TAG_SET_READY =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setReadyForReview";
  public static final String TAG_SET_TOPIC = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setTopic";
  public static final String TAG_SET_WIP = AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "setWorkInProgress";
  public static final String TAG_UNSET_PRIVATE =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "unsetPrivate";
  public static final String TAG_UPLOADED_PATCH_SET =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "newPatchSet";
  public static final String TAG_UPLOADED_WIP_PATCH_SET =
      AUTOGENERATED_BY_GERRIT_TAG_PREFIX + "newWipPatchSet";

  private final AccountTemplateUtil accountTemplateUtil;

  @Inject
  ChangeMessagesUtil(AccountTemplateUtil accountTemplateUtil) {
    this.accountTemplateUtil = accountTemplateUtil;
  }

  /**
   * Sets {@code messageTemplate} and {@code tag}, that should be applied by the {@code update}.
   *
   * <p>The {@code messageTemplate} is persisted in storage and should not contain user identifiable
   * information. See {@link ChangeMessage}.
   *
   * @param update update that sets {@code messageTemplate}.
   * @param messageTemplate message in template form, that should be applied by the update.
   * @param tag tag that should be applied by the update.
   * @return message built from {@code messageTemplate}. Templates are replaced, so it might contain
   *     user identifiable information.
   */
  public String setChangeMessage(
      ChangeUpdate update, String messageTemplate, @Nullable String tag) {
    update.setChangeMessage(messageTemplate);
    update.setTag(tag);
    return accountTemplateUtil.replaceTemplates(messageTemplate);
  }

  /** See {@link #setChangeMessage(ChangeUpdate, String, String)}. */
  public String setChangeMessage(ChangeContext ctx, String messageTemplate, @Nullable String tag) {
    return setChangeMessage(
        ctx.getUpdate(ctx.getChange().currentPatchSetId()), messageTemplate, tag);
  }

  public static String uploadedPatchSetTag(boolean workInProgress) {
    return workInProgress ? TAG_UPLOADED_WIP_PATCH_SET : TAG_UPLOADED_PATCH_SET;
  }

  /**
   * Returns {@link ChangeMessage}s from {@link ChangeNotes}, loads {@link ChangeNotes} from data
   * storage (cache or NoteDB), if it was not loaded yet.
   */
  public List<ChangeMessage> byChange(ChangeNotes notes) {
    return notes.load().getChangeMessages();
  }

  /**
   * Replace an existing change message with the provided new message.
   *
   * <p>The ID of a change message is different between NoteDb and ReviewDb. In NoteDb, it's the
   * commit SHA-1, but in ReviewDb it was generated randomly. Taking the target message as an index
   * rather than an ID allowed us to delete the message from both NoteDb and ReviewDb.
   *
   * @param update change update.
   * @param targetMessageId the id of the target change message.
   * @param newMessage the new message which is going to replace the old.
   */
  public void replaceChangeMessage(ChangeUpdate update, String targetMessageId, String newMessage) {
    update.deleteChangeMessageByRewritingHistory(targetMessageId, newMessage);
  }

  /**
   * Determines whether the tag starts with the autogenerated prefix
   *
   * @param tag value of a tag, or null.
   */
  public static boolean isAutogenerated(@Nullable String tag) {
    return tag != null && tag.startsWith(AUTOGENERATED_TAG_PREFIX);
  }

  public static boolean isAutogeneratedByGerrit(@Nullable String tag) {
    return tag != null && tag.startsWith(AUTOGENERATED_BY_GERRIT_TAG_PREFIX);
  }

  public static ChangeMessageInfo createChangeMessageInfo(
      ChangeMessage message, AccountLoader accountLoader) {
    PatchSet.Id patchNum = message.getPatchSetId();
    ChangeMessageInfo cmi = new ChangeMessageInfo();
    cmi.id = message.getKey().uuid();
    cmi.author = accountLoader.get(message.getAuthor());
    cmi.setDate(message.getWrittenOn());
    cmi.message = message.getMessage();
    cmi.tag = message.getTag();
    cmi._revisionNumber = patchNum != null ? patchNum.get() : null;
    Account.Id realAuthor = message.getRealAuthor();
    if (realAuthor != null) {
      cmi.realAuthor = accountLoader.get(realAuthor);
    }
    cmi.accountsInMessage =
        AccountTemplateUtil.parseTemplates(message.getMessage()).stream()
            .map(accountLoader::get)
            .collect(toImmutableSet());
    return cmi;
  }

  /**
   * {@link ChangeMessage} is served in template form to {@link
   * com.google.gerrit.extensions.api.changes.ChangeApi}. Serve message with replaced templates to
   * the legacy {@link com.google.gerrit.extensions.api.changes.ChangeMessageApi} endpoints.
   * TODO(mariasavtchouk): remove this, after {@link
   * com.google.gerrit.extensions.api.changes.ChangeMessageApi} is deprecated (gate with
   * experiment).
   */
  public ChangeMessageInfo createChangeMessageInfoWithReplacedTemplates(
      ChangeMessage message, AccountLoader accountLoader) {
    ChangeMessageInfo changeMessageInfo = createChangeMessageInfo(message, accountLoader);
    changeMessageInfo.message = accountTemplateUtil.replaceTemplates(message.getMessage());
    return changeMessageInfo;
  }
}