Line data Source code
1 : // Copyright (C) 2020 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 License. 14 : 15 : package com.google.gerrit.server.account; 16 : 17 : import static com.google.common.collect.ImmutableSet.toImmutableSet; 18 : 19 : import com.google.auto.value.AutoValue; 20 : import com.google.common.base.Enums; 21 : import com.google.common.base.Strings; 22 : import com.google.common.collect.ImmutableMap; 23 : import com.google.common.collect.ImmutableSet; 24 : import com.google.gerrit.common.UsedAt; 25 : import com.google.gerrit.entities.Account; 26 : import com.google.gerrit.entities.NotifyConfig; 27 : import com.google.gerrit.entities.Project; 28 : import com.google.gerrit.proto.Protos; 29 : import com.google.gerrit.server.cache.proto.Cache; 30 : import com.google.gerrit.server.cache.serialize.CacheSerializer; 31 : import com.google.gerrit.server.cache.serialize.ObjectIdConverter; 32 : import com.google.gerrit.server.config.CachedPreferences; 33 : import java.time.Instant; 34 : import java.util.Map; 35 : import org.eclipse.jgit.lib.ObjectId; 36 : 37 : /** Details of an account that are cached persistently in {@link AccountCache}. */ 38 : @UsedAt(UsedAt.Project.GOOGLE) 39 : @AutoValue 40 151 : public abstract class CachedAccountDetails { 41 : @AutoValue 42 151 : public abstract static class Key { 43 : static Key create(Account.Id accountId, ObjectId id) { 44 151 : return new AutoValue_CachedAccountDetails_Key(accountId, id.copy()); 45 : } 46 : 47 : /** Identifier of the account. */ 48 : abstract Account.Id accountId(); 49 : 50 : /** 51 : * Git revision at which the account was loaded. Corresponds to a revision on the account ref 52 : * ({@code refs/users/<sharded-id>}). 53 : */ 54 : abstract ObjectId id(); 55 : 56 : /** Serializer used to read this entity from and write it to a persistent storage. */ 57 152 : enum Serializer implements CacheSerializer<Key> { 58 152 : INSTANCE; 59 : 60 : @Override 61 : public byte[] serialize(Key object) { 62 15 : return Protos.toByteArray( 63 15 : Cache.AccountKeyProto.newBuilder() 64 15 : .setAccountId(object.accountId().get()) 65 15 : .setId(ObjectIdConverter.create().toByteString(object.id())) 66 15 : .build()); 67 : } 68 : 69 : @Override 70 : public Key deserialize(byte[] in) { 71 1 : Cache.AccountKeyProto proto = Protos.parseUnchecked(Cache.AccountKeyProto.parser(), in); 72 1 : return Key.create( 73 1 : Account.id(proto.getAccountId()), 74 1 : ObjectIdConverter.create().fromByteString(proto.getId())); 75 : } 76 : } 77 : } 78 : 79 : /** Essential attributes of the account, such as name or registration time. */ 80 : abstract Account account(); 81 : 82 : /** Projects that the user has configured to watch. */ 83 : abstract ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> 84 : projectWatches(); 85 : 86 : /** Preferences that this user has. Serialized as Git-config style string. */ 87 : abstract CachedPreferences preferences(); 88 : 89 : static CachedAccountDetails create( 90 : Account account, 91 : ImmutableMap<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> 92 : projectWatches, 93 : CachedPreferences preferences) { 94 151 : return new AutoValue_CachedAccountDetails(account, projectWatches, preferences); 95 : } 96 : 97 : /** Serializer used to read this entity from and write it to a persistent storage. */ 98 152 : enum Serializer implements CacheSerializer<CachedAccountDetails> { 99 152 : INSTANCE; 100 : 101 : @Override 102 : public byte[] serialize(CachedAccountDetails cachedAccountDetails) { 103 16 : Cache.AccountDetailsProto.Builder serialized = Cache.AccountDetailsProto.newBuilder(); 104 : // We don't care about the difference of empty strings and null in the Account entity. 105 16 : Account account = cachedAccountDetails.account(); 106 : Cache.AccountProto.Builder accountProto = 107 16 : Cache.AccountProto.newBuilder() 108 16 : .setId(account.id().get()) 109 16 : .setRegisteredOn(account.registeredOn().toEpochMilli()) 110 16 : .setInactive(account.inactive()) 111 16 : .setFullName(Strings.nullToEmpty(account.fullName())) 112 16 : .setDisplayName(Strings.nullToEmpty(account.displayName())) 113 16 : .setPreferredEmail(Strings.nullToEmpty(account.preferredEmail())) 114 16 : .setStatus(Strings.nullToEmpty(account.status())) 115 16 : .setMetaId(Strings.nullToEmpty(account.metaId())); 116 16 : serialized.setAccount(accountProto); 117 : 118 : for (Map.Entry<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> watch : 119 16 : cachedAccountDetails.projectWatches().entrySet()) { 120 : Cache.ProjectWatchProto.Builder proto = 121 1 : Cache.ProjectWatchProto.newBuilder().setProject(watch.getKey().project().get()); 122 1 : if (watch.getKey().filter() != null) { 123 1 : proto.setFilter(watch.getKey().filter()); 124 : } 125 1 : watch 126 1 : .getValue() 127 1 : .forEach( 128 : n -> 129 1 : proto.addNotifyType( 130 1 : Enums.stringConverter(NotifyConfig.NotifyType.class).reverse().convert(n))); 131 1 : serialized.addProjectWatchProto(proto); 132 1 : } 133 : 134 16 : serialized.setUserPreferences(cachedAccountDetails.preferences().config()); 135 16 : return Protos.toByteArray(serialized.build()); 136 : } 137 : 138 : @Override 139 : public CachedAccountDetails deserialize(byte[] in) { 140 : Cache.AccountDetailsProto proto = 141 2 : Protos.parseUnchecked(Cache.AccountDetailsProto.parser(), in); 142 2 : Account account = 143 2 : Account.builder( 144 2 : Account.id(proto.getAccount().getId()), 145 2 : Instant.ofEpochMilli(proto.getAccount().getRegisteredOn())) 146 2 : .setFullName(Strings.emptyToNull(proto.getAccount().getFullName())) 147 2 : .setDisplayName(Strings.emptyToNull(proto.getAccount().getDisplayName())) 148 2 : .setPreferredEmail(Strings.emptyToNull(proto.getAccount().getPreferredEmail())) 149 2 : .setInactive(proto.getAccount().getInactive()) 150 2 : .setStatus(Strings.emptyToNull(proto.getAccount().getStatus())) 151 2 : .setMetaId(Strings.emptyToNull(proto.getAccount().getMetaId())) 152 2 : .build(); 153 : 154 : ImmutableMap.Builder<ProjectWatches.ProjectWatchKey, ImmutableSet<NotifyConfig.NotifyType>> 155 2 : projectWatches = ImmutableMap.builder(); 156 2 : proto.getProjectWatchProtoList().stream() 157 2 : .forEach( 158 : p -> 159 1 : projectWatches.put( 160 1 : ProjectWatches.ProjectWatchKey.create( 161 1 : Project.nameKey(p.getProject()), p.getFilter()), 162 1 : p.getNotifyTypeList().stream() 163 1 : .map(e -> Enums.stringConverter(NotifyConfig.NotifyType.class).convert(e)) 164 1 : .collect(toImmutableSet()))); 165 : 166 2 : return CachedAccountDetails.create( 167 : account, 168 2 : projectWatches.build(), 169 2 : CachedPreferences.fromString(proto.getUserPreferences())); 170 : } 171 : } 172 : }