Line data Source code
1 : // Copyright (C) 2017 The Android Open Source Project
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the Licens
14 :
15 : package com.google.gerrit.server.account;
16 :
17 : import com.google.auto.value.AutoValue;
18 : import com.google.common.base.Strings;
19 : import com.google.common.collect.ImmutableMap;
20 : import com.google.common.collect.ImmutableSet;
21 : import com.google.gerrit.common.Nullable;
22 : import com.google.gerrit.entities.Account;
23 : import com.google.gerrit.entities.NotifyConfig.NotifyType;
24 : import com.google.gerrit.extensions.client.DiffPreferencesInfo;
25 : import com.google.gerrit.extensions.client.EditPreferencesInfo;
26 : import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
27 : import com.google.gerrit.server.account.ProjectWatches.ProjectWatchKey;
28 : import com.google.gerrit.server.account.externalids.DuplicateExternalIdKeyException;
29 : import com.google.gerrit.server.account.externalids.ExternalId;
30 : import java.util.Collection;
31 : import java.util.Map;
32 : import java.util.Optional;
33 : import java.util.Set;
34 :
35 : /**
36 : * Data holder for updates to be applied to an account.
37 : *
38 : * <p>Instances of this type are passed to {@link AccountsUpdate}, which modifies the account
39 : * accordingly.
40 : *
41 : * <p>Updates can be applied to account properties (name, email etc.), external IDs, preferences
42 : * (general, diff and edit preferences) and project watches. The account ID and the registration
43 : * date cannot be updated.
44 : *
45 : * <p>For the account properties there are getters in this class and setters in the {@link Builder}
46 : * that correspond to the fields in {@link Account}.
47 : */
48 : @AutoValue
49 151 : public abstract class AccountDelta {
50 : public static Builder builder() {
51 151 : return new Builder.WrapperThatConvertsNullStringArgsToEmptyStrings(
52 : new AutoValue_AccountDelta.Builder());
53 : }
54 :
55 : /**
56 : * Returns the new value for the full name.
57 : *
58 : * @return the new value for the full name, {@code Optional#empty()} if the full name is not being
59 : * updated, {@code Optional#of("")} if the full name is unset, the wrapped value is never
60 : * {@code null}
61 : */
62 : public abstract Optional<String> getFullName();
63 :
64 : /**
65 : * Returns the new value for the display name.
66 : *
67 : * @return the new value for the display name, {@code Optional#empty()} if the display name is not
68 : * being updated, {@code Optional#of("")} if the display name is unset, the wrapped value is
69 : * never {@code null}
70 : */
71 : public abstract Optional<String> getDisplayName();
72 :
73 : /**
74 : * Returns the new value for the preferred email.
75 : *
76 : * @return the new value for the preferred email, {@code Optional#empty()} if the preferred email
77 : * is not being updated, {@code Optional#of("")} if the preferred email is unset, the wrapped
78 : * value is never {@code null}
79 : */
80 : public abstract Optional<String> getPreferredEmail();
81 :
82 : /**
83 : * Returns the new value for the active flag.
84 : *
85 : * @return the new value for the active flag, {@code Optional#empty()} if the active flag is not
86 : * being updated, the wrapped value is never {@code null}
87 : */
88 : public abstract Optional<Boolean> getActive();
89 :
90 : /**
91 : * Returns the new value for the status.
92 : *
93 : * @return the new value for the status, {@code Optional#empty()} if the status is not being
94 : * updated, {@code Optional#of("")} if the status is unset, the wrapped value is never {@code
95 : * null}
96 : */
97 : public abstract Optional<String> getStatus();
98 :
99 : /**
100 : * Returns external IDs that should be newly created for the account.
101 : *
102 : * @return external IDs that should be newly created for the account
103 : */
104 : public abstract ImmutableSet<ExternalId> getCreatedExternalIds();
105 :
106 : /**
107 : * Returns external IDs that should be updated for the account.
108 : *
109 : * @return external IDs that should be updated for the account
110 : */
111 : public abstract ImmutableSet<ExternalId> getUpdatedExternalIds();
112 :
113 : /**
114 : * Returns external IDs that should be deleted for the account.
115 : *
116 : * @return external IDs that should be deleted for the account
117 : */
118 : public abstract ImmutableSet<ExternalId> getDeletedExternalIds();
119 :
120 : /**
121 : * Returns external IDs that should be updated for the account.
122 : *
123 : * @return external IDs that should be updated for the account
124 : */
125 : public abstract ImmutableMap<ProjectWatchKey, Set<NotifyType>> getUpdatedProjectWatches();
126 :
127 : /**
128 : * Returns project watches that should be deleted for the account.
129 : *
130 : * @return project watches that should be deleted for the account
131 : */
132 : public abstract ImmutableSet<ProjectWatchKey> getDeletedProjectWatches();
133 :
134 : /**
135 : * Returns the new value for the general preferences.
136 : *
137 : * <p>Only preferences that are non-null in the returned GeneralPreferencesInfo should be updated.
138 : *
139 : * @return the new value for the general preferences, {@code Optional#empty()} if the general
140 : * preferences are not being updated, the wrapped value is never {@code null}
141 : */
142 : public abstract Optional<GeneralPreferencesInfo> getGeneralPreferences();
143 :
144 : /**
145 : * Returns the new value for the diff preferences.
146 : *
147 : * <p>Only preferences that are non-null in the returned DiffPreferencesInfo should be updated.
148 : *
149 : * @return the new value for the diff preferences, {@code Optional#empty()} if the diff
150 : * preferences are not being updated, the wrapped value is never {@code null}
151 : */
152 : public abstract Optional<DiffPreferencesInfo> getDiffPreferences();
153 :
154 : /**
155 : * Returns the new value for the edit preferences.
156 : *
157 : * <p>Only preferences that are non-null in the returned DiffPreferencesInfo should be updated.
158 : *
159 : * @return the new value for the edit preferences, {@code Optional#empty()} if the edit
160 : * preferences are not being updated, the wrapped value is never {@code null}
161 : */
162 : public abstract Optional<EditPreferencesInfo> getEditPreferences();
163 :
164 : /**
165 : * Class to build an {@link AccountDelta}.
166 : *
167 : * <p>Account data is only updated if the corresponding setter is invoked. If a setter is not
168 : * invoked the corresponding data stays unchanged. To unset string values the setter can be
169 : * invoked with either {@code null} or an empty string ({@code null} is converted to an empty
170 : * string by using the {@link WrapperThatConvertsNullStringArgsToEmptyStrings} wrapper, see {@link
171 : * AccountDelta#builder()}).
172 : */
173 : @AutoValue.Builder
174 151 : public abstract static class Builder {
175 : /**
176 : * Sets a new full name for the account.
177 : *
178 : * @param fullName the new full name, if {@code null} or empty string the full name is unset
179 : */
180 : public abstract Builder setFullName(@Nullable String fullName);
181 :
182 : /**
183 : * Sets a new display name for the account.
184 : *
185 : * @param displayName the new display name, if {@code null} or empty string the display name is
186 : * unset
187 : */
188 : public abstract Builder setDisplayName(@Nullable String displayName);
189 :
190 : /**
191 : * Sets a new preferred email for the account.
192 : *
193 : * @param preferredEmail the new preferred email, if {@code null} or empty string the preferred
194 : * email is unset
195 : */
196 : public abstract Builder setPreferredEmail(@Nullable String preferredEmail);
197 :
198 : /**
199 : * Sets the active flag for the account.
200 : *
201 : * @param active {@code true} if the account should be set to active, {@code false} if the
202 : * account should be set to inactive
203 : */
204 : public abstract Builder setActive(boolean active);
205 :
206 : /**
207 : * Sets a new status for the account.
208 : *
209 : * @param status the new status, if {@code null} or empty string the status is unset
210 : */
211 : public abstract Builder setStatus(@Nullable String status);
212 :
213 : /**
214 : * Returns a builder for the set of created external IDs.
215 : *
216 : * @return builder for the set of created external IDs.
217 : */
218 : abstract ImmutableSet.Builder<ExternalId> createdExternalIdsBuilder();
219 :
220 : /**
221 : * Adds a new external ID for the account.
222 : *
223 : * <p>The account ID of the external ID must match the account ID of the account that is
224 : * updated.
225 : *
226 : * <p>If an external ID with the same ID already exists the account update will fail with {@link
227 : * DuplicateExternalIdKeyException}.
228 : *
229 : * @param extId external ID that should be added
230 : * @return the builder
231 : */
232 : public Builder addExternalId(ExternalId extId) {
233 23 : return addExternalIds(ImmutableSet.of(extId));
234 : }
235 :
236 : /**
237 : * Adds new external IDs for the account.
238 : *
239 : * <p>The account IDs of the external IDs must match the account ID of the account that is
240 : * updated.
241 : *
242 : * <p>If any of the external ID keys already exists, the insert fails with {@link
243 : * DuplicateExternalIdKeyException}.
244 : *
245 : * @param extIds external IDs that should be added
246 : * @return the builder
247 : */
248 : public Builder addExternalIds(Collection<ExternalId> extIds) {
249 151 : createdExternalIdsBuilder().addAll(extIds);
250 151 : return this;
251 : }
252 :
253 : /**
254 : * Returns a builder for the set of updated external IDs.
255 : *
256 : * @return builder for the set of updated external IDs.
257 : */
258 : abstract ImmutableSet.Builder<ExternalId> updatedExternalIdsBuilder();
259 :
260 : /**
261 : * Updates an external ID for the account.
262 : *
263 : * <p>The account ID of the external ID must match the account ID of the account that is
264 : * updated.
265 : *
266 : * <p>If no external ID with the ID exists the external ID is created.
267 : *
268 : * @param extId external ID that should be updated
269 : * @return the builder
270 : */
271 : public Builder updateExternalId(ExternalId extId) {
272 7 : return updateExternalIds(ImmutableSet.of(extId));
273 : }
274 :
275 : /**
276 : * Updates external IDs for the account.
277 : *
278 : * <p>The account IDs of the external IDs must match the account ID of the account that is
279 : * updated.
280 : *
281 : * <p>If any of the external IDs already exists, it is overwritten. New external IDs are
282 : * inserted.
283 : *
284 : * @param extIds external IDs that should be updated
285 : * @return the builder
286 : */
287 : public Builder updateExternalIds(Collection<ExternalId> extIds) {
288 7 : updatedExternalIdsBuilder().addAll(extIds);
289 7 : return this;
290 : }
291 :
292 : /**
293 : * Returns a builder for the set of deleted external IDs.
294 : *
295 : * @return builder for the set of deleted external IDs.
296 : */
297 : abstract ImmutableSet.Builder<ExternalId> deletedExternalIdsBuilder();
298 :
299 : /**
300 : * Deletes an external ID for the account.
301 : *
302 : * <p>The account ID of the external ID must match the account ID of the account that is
303 : * updated.
304 : *
305 : * <p>If no external ID with the ID exists this is a no-op.
306 : *
307 : * @param extId external ID that should be deleted
308 : * @return the builder
309 : */
310 : public Builder deleteExternalId(ExternalId extId) {
311 2 : return deleteExternalIds(ImmutableSet.of(extId));
312 : }
313 :
314 : /**
315 : * Deletes external IDs for the account.
316 : *
317 : * <p>The account IDs of the external IDs must match the account ID of the account that is
318 : * updated.
319 : *
320 : * <p>For non-existing external IDs this is a no-op.
321 : *
322 : * @param extIds external IDs that should be deleted
323 : * @return the builder
324 : */
325 : public Builder deleteExternalIds(Collection<ExternalId> extIds) {
326 9 : deletedExternalIdsBuilder().addAll(extIds);
327 9 : return this;
328 : }
329 :
330 : /**
331 : * Replaces an external ID.
332 : *
333 : * @param extIdToDelete external ID that should be deleted
334 : * @param extIdToAdd external ID that should be added
335 : * @return the builder
336 : */
337 : public Builder replaceExternalId(ExternalId extIdToDelete, ExternalId extIdToAdd) {
338 5 : return replaceExternalIds(ImmutableSet.of(extIdToDelete), ImmutableSet.of(extIdToAdd));
339 : }
340 :
341 : /**
342 : * Replaces an external IDs.
343 : *
344 : * @param extIdsToDelete external IDs that should be deleted
345 : * @param extIdsToAdd external IDs that should be added
346 : * @return the builder
347 : */
348 : public Builder replaceExternalIds(
349 : Collection<ExternalId> extIdsToDelete, Collection<ExternalId> extIdsToAdd) {
350 6 : return deleteExternalIds(extIdsToDelete).addExternalIds(extIdsToAdd);
351 : }
352 :
353 : /**
354 : * Returns a builder for the map of updated project watches.
355 : *
356 : * @return builder for the map of updated project watches.
357 : */
358 : abstract ImmutableMap.Builder<ProjectWatchKey, Set<NotifyType>> updatedProjectWatchesBuilder();
359 :
360 : /**
361 : * Updates a project watch for the account.
362 : *
363 : * <p>If no project watch with the key exists the project watch is created.
364 : *
365 : * @param projectWatchKey key of the project watch that should be updated
366 : * @param notifyTypes the notify types that should be set for the project watch
367 : * @return the builder
368 : */
369 : public Builder updateProjectWatch(
370 : ProjectWatchKey projectWatchKey, Set<NotifyType> notifyTypes) {
371 0 : return updateProjectWatches(ImmutableMap.of(projectWatchKey, notifyTypes));
372 : }
373 :
374 : /**
375 : * Updates project watches for the account.
376 : *
377 : * <p>If any of the project watches already exists, it is overwritten. New project watches are
378 : * inserted.
379 : *
380 : * @param projectWatches project watches that should be updated
381 : * @return the builder
382 : */
383 : public Builder updateProjectWatches(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
384 16 : updatedProjectWatchesBuilder().putAll(projectWatches);
385 16 : return this;
386 : }
387 :
388 : /**
389 : * Returns a builder for the set of deleted project watches.
390 : *
391 : * @return builder for the set of deleted project watches.
392 : */
393 : abstract ImmutableSet.Builder<ProjectWatchKey> deletedProjectWatchesBuilder();
394 :
395 : /**
396 : * Deletes a project watch for the account.
397 : *
398 : * <p>If no project watch with the ID exists this is a no-op.
399 : *
400 : * @param projectWatch project watch that should be deleted
401 : * @return the builder
402 : */
403 : public Builder deleteProjectWatch(ProjectWatchKey projectWatch) {
404 0 : return deleteProjectWatches(ImmutableSet.of(projectWatch));
405 : }
406 :
407 : /**
408 : * Deletes project watches for the account.
409 : *
410 : * <p>For non-existing project watches this is a no-op.
411 : *
412 : * @param projectWatches project watches that should be deleted
413 : * @return the builder
414 : */
415 : public Builder deleteProjectWatches(Collection<ProjectWatchKey> projectWatches) {
416 2 : deletedProjectWatchesBuilder().addAll(projectWatches);
417 2 : return this;
418 : }
419 :
420 : /**
421 : * Sets the general preferences for the account.
422 : *
423 : * <p>Updates any preference that is non-null in the provided GeneralPreferencesInfo.
424 : *
425 : * @param generalPreferences the general preferences that should be set
426 : * @return the builder
427 : */
428 : public abstract Builder setGeneralPreferences(GeneralPreferencesInfo generalPreferences);
429 :
430 : /**
431 : * Sets the diff preferences for the account.
432 : *
433 : * <p>Updates any preference that is non-null in the provided DiffPreferencesInfo.
434 : *
435 : * @param diffPreferences the diff preferences that should be set
436 : * @return the builder
437 : */
438 : public abstract Builder setDiffPreferences(DiffPreferencesInfo diffPreferences);
439 :
440 : /**
441 : * Sets the edit preferences for the account.
442 : *
443 : * <p>Updates any preference that is non-null in the provided EditPreferencesInfo.
444 : *
445 : * @param editPreferences the edit preferences that should be set
446 : * @return the builder
447 : */
448 : public abstract Builder setEditPreferences(EditPreferencesInfo editPreferences);
449 :
450 : /** Builds the instance. */
451 : public abstract AccountDelta build();
452 :
453 : /**
454 : * Wrapper for {@link Builder} that converts {@code null} string arguments to empty strings for
455 : * all setter methods. This allows us to treat setter invocations with a {@code null} string
456 : * argument as signal to unset the corresponding field. E.g. for a builder method {@code
457 : * setX(String)} the following semantics apply:
458 : *
459 : * <ul>
460 : * <li>Method is not invoked: X stays unchanged, X is stored as {@code Optional.empty()}.
461 : * <li>Argument is a non-empty string Y: X is updated to the Y, X is stored as {@code
462 : * Optional.of(Y)}.
463 : * <li>Argument is an empty string: X is unset, X is stored as {@code Optional.of("")}
464 : * <li>Argument is {@code null}: X is unset, X is stored as {@code Optional.of("")} (since the
465 : * wrapper converts {@code null} to an empty string)
466 : * </ul>
467 : *
468 : * Without the wrapper calling {@code setX(null)} would fail with a {@link
469 : * NullPointerException}. Hence all callers would need to take care to call {@link
470 : * Strings#nullToEmpty(String)} for all string arguments and likely it would be forgotten in
471 : * some places.
472 : *
473 : * <p>This means the stored values are interpreted like this:
474 : *
475 : * <ul>
476 : * <li>{@code Optional.empty()}: property stays unchanged
477 : * <li>{@code Optional.of(<non-empty-string>)}: property is updated
478 : * <li>{@code Optional.of("")}: property is unset
479 : * </ul>
480 : *
481 : * This wrapper forwards all method invocations to the wrapped {@link Builder} instance that was
482 : * created by AutoValue. For methods that return the AutoValue {@link Builder} instance the
483 : * return value is replaced with the wrapper instance so that all chained calls go through the
484 : * wrapper.
485 : */
486 : private static class WrapperThatConvertsNullStringArgsToEmptyStrings extends Builder {
487 : private final Builder delegate;
488 :
489 151 : private WrapperThatConvertsNullStringArgsToEmptyStrings(Builder delegate) {
490 151 : this.delegate = delegate;
491 151 : }
492 :
493 : @Override
494 : public Builder setFullName(String fullName) {
495 151 : delegate.setFullName(Strings.nullToEmpty(fullName));
496 151 : return this;
497 : }
498 :
499 : @Override
500 : public Builder setDisplayName(String displayName) {
501 138 : delegate.setDisplayName(Strings.nullToEmpty(displayName));
502 138 : return this;
503 : }
504 :
505 : @Override
506 : public Builder setPreferredEmail(String preferredEmail) {
507 151 : delegate.setPreferredEmail(Strings.nullToEmpty(preferredEmail));
508 151 : return this;
509 : }
510 :
511 : @Override
512 : public Builder setActive(boolean active) {
513 16 : delegate.setActive(active);
514 16 : return this;
515 : }
516 :
517 : @Override
518 : public Builder setStatus(String status) {
519 2 : delegate.setStatus(Strings.nullToEmpty(status));
520 2 : return this;
521 : }
522 :
523 : @Override
524 : public AccountDelta build() {
525 151 : return delegate.build();
526 : }
527 :
528 : @Override
529 : ImmutableSet.Builder<ExternalId> createdExternalIdsBuilder() {
530 0 : return delegate.createdExternalIdsBuilder();
531 : }
532 :
533 : @Override
534 : public Builder addExternalIds(Collection<ExternalId> extIds) {
535 151 : delegate.addExternalIds(extIds);
536 151 : return this;
537 : }
538 :
539 : @Override
540 : ImmutableSet.Builder<ExternalId> updatedExternalIdsBuilder() {
541 0 : return delegate.updatedExternalIdsBuilder();
542 : }
543 :
544 : @Override
545 : public Builder updateExternalIds(Collection<ExternalId> extIds) {
546 7 : delegate.updateExternalIds(extIds);
547 7 : return this;
548 : }
549 :
550 : @Override
551 : ImmutableSet.Builder<ExternalId> deletedExternalIdsBuilder() {
552 0 : return delegate.deletedExternalIdsBuilder();
553 : }
554 :
555 : @Override
556 : public Builder deleteExternalIds(Collection<ExternalId> extIds) {
557 9 : delegate.deleteExternalIds(extIds);
558 9 : return this;
559 : }
560 :
561 : @Override
562 : ImmutableMap.Builder<ProjectWatchKey, Set<NotifyType>> updatedProjectWatchesBuilder() {
563 0 : return delegate.updatedProjectWatchesBuilder();
564 : }
565 :
566 : @Override
567 : public Builder updateProjectWatches(Map<ProjectWatchKey, Set<NotifyType>> projectWatches) {
568 16 : delegate.updateProjectWatches(projectWatches);
569 16 : return this;
570 : }
571 :
572 : @Override
573 : ImmutableSet.Builder<ProjectWatchKey> deletedProjectWatchesBuilder() {
574 0 : return delegate.deletedProjectWatchesBuilder();
575 : }
576 :
577 : @Override
578 : public Builder deleteProjectWatches(Collection<ProjectWatchKey> projectWatches) {
579 2 : delegate.deleteProjectWatches(projectWatches);
580 2 : return this;
581 : }
582 :
583 : @Override
584 : public Builder setGeneralPreferences(GeneralPreferencesInfo generalPreferences) {
585 8 : delegate.setGeneralPreferences(generalPreferences);
586 8 : return this;
587 : }
588 :
589 : @Override
590 : public Builder setDiffPreferences(DiffPreferencesInfo diffPreferences) {
591 1 : delegate.setDiffPreferences(diffPreferences);
592 1 : return this;
593 : }
594 :
595 : @Override
596 : public Builder setEditPreferences(EditPreferencesInfo editPreferences) {
597 1 : delegate.setEditPreferences(editPreferences);
598 1 : return this;
599 : }
600 : }
601 : }
602 : }
|