1   /*
2    * Copyright 2019 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.server.metadata;
18  
19  import static com.google.common.collect.ImmutableMap.toImmutableMap;
20  import static java.util.Objects.requireNonNull;
21  
22  import java.util.Map;
23  import java.util.Map.Entry;
24  import java.util.Objects;
25  
26  import javax.annotation.Nullable;
27  
28  import com.fasterxml.jackson.annotation.JsonCreator;
29  import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
30  import com.fasterxml.jackson.annotation.JsonInclude;
31  import com.fasterxml.jackson.annotation.JsonInclude.Include;
32  import com.fasterxml.jackson.annotation.JsonProperty;
33  import com.google.common.base.MoreObjects;
34  import com.google.common.collect.ImmutableMap;
35  
36  import com.linecorp.centraldogma.common.RepositoryNotFoundException;
37  import com.linecorp.centraldogma.server.storage.project.Project;
38  import com.linecorp.centraldogma.server.storage.repository.HasWeight;
39  
40  /**
41   * Specifies details of a {@link Project}.
42   */
43  @JsonIgnoreProperties(ignoreUnknown = true)
44  @JsonInclude(Include.NON_NULL)
45  public class ProjectMetadata implements Identifiable, HasWeight {
46  
47      /**
48       * A project name.
49       */
50      private final String name;
51  
52      /**
53       * Repositories of this project.
54       */
55      private final Map<String, RepositoryMetadata> repos;
56  
57      /**
58       * Members of this project.
59       */
60      private final Map<String, Member> members;
61  
62      /**
63       * Tokens which belong to this project.
64       */
65      private final Map<String, TokenRegistration> tokens;
66  
67      /**
68       * Specifies when this project is created by whom.
69       */
70      private final UserAndTimestamp creation;
71  
72      /**
73       * Specifies when this project is removed by whom.
74       */
75      @Nullable
76      private final UserAndTimestamp removal;
77  
78      /**
79       * Creates a new instance.
80       */
81      @JsonCreator
82      public ProjectMetadata(@JsonProperty("name") String name,
83                             @JsonProperty("repos") Map<String, RepositoryMetadata> repos,
84                             @JsonProperty("members") Map<String, Member> members,
85                             @JsonProperty("tokens") Map<String, TokenRegistration> tokens,
86                             @JsonProperty("creation") UserAndTimestamp creation,
87                             @JsonProperty("removal") @Nullable UserAndTimestamp removal) {
88          this.name = requireNonNull(name, "name");
89          this.repos = ImmutableMap.copyOf(requireNonNull(repos, "repos"));
90          this.members = ImmutableMap.copyOf(requireNonNull(members, "members"));
91          this.tokens = ImmutableMap.copyOf(requireNonNull(tokens, "tokens"));
92          this.creation = requireNonNull(creation, "creation");
93          this.removal = removal;
94      }
95  
96      @Override
97      public String id() {
98          return name;
99      }
100 
101     /**
102      * Returns the project name.
103      */
104     @JsonProperty
105     public String name() {
106         return name;
107     }
108 
109     /**
110      * Returns the metadata of the repositories in this project.
111      */
112     @JsonProperty
113     public Map<String, RepositoryMetadata> repos() {
114         return repos;
115     }
116 
117     /**
118      * Returns the {@link Member}s of this project.
119      */
120     @JsonProperty
121     public Map<String, Member> members() {
122         return members;
123     }
124 
125     /**
126      * Returns the {@link TokenRegistration}s of this project.
127      */
128     @JsonProperty
129     public Map<String, TokenRegistration> tokens() {
130         return tokens;
131     }
132 
133     /**
134      * Returns who created this project when.
135      */
136     @JsonProperty
137     public UserAndTimestamp creation() {
138         return creation;
139     }
140 
141     /**
142      * Returns who removed this project when.
143      */
144     @Nullable
145     @JsonProperty
146     public UserAndTimestamp removal() {
147         return removal;
148     }
149 
150     /**
151      * Returns the {@link RepositoryMetadata} of the specified repository in this project.
152      */
153     public RepositoryMetadata repo(String repoName) {
154         final RepositoryMetadata repositoryMetadata =
155                 repos.get(requireNonNull(repoName, "repoName"));
156         if (repositoryMetadata != null) {
157             return repositoryMetadata;
158         }
159         throw new RepositoryNotFoundException(repoName);
160     }
161 
162     /**
163      * Returns the {@link Member} of the specified ID in this project.
164      */
165     public Member member(String memberId) {
166         final Member member = memberOrDefault(memberId, null);
167         if (member != null) {
168             return member;
169         }
170         throw new MemberNotFoundException(memberId, name());
171     }
172 
173     /**
174      * Returns the {@link Member} of the specified ID in this project.
175      * {@code defaultMember} is returned if there is no such member.
176      */
177     @Nullable
178     public Member memberOrDefault(String memberId, @Nullable Member defaultMember) {
179         final Member member = members.get(requireNonNull(memberId, "memberId"));
180         if (member != null) {
181             return member;
182         }
183         return defaultMember;
184     }
185 
186     @Override
187     public int weight() {
188         int weight = name().length();
189         for (RepositoryMetadata repo : repos.values()) {
190             weight += repo.weight();
191         }
192         for (Member member : members.values()) {
193             weight += member.weight();
194         }
195         for (TokenRegistration token : tokens.values()) {
196             weight += token.weight();
197         }
198 
199         return weight;
200     }
201 
202     @Override
203     public boolean equals(Object o) {
204         if (this == o) {
205             return true;
206         }
207         if (!(o instanceof ProjectMetadata)) {
208             return false;
209         }
210         final ProjectMetadata that = (ProjectMetadata) o;
211         return name.equals(that.name) &&
212                repos.equals(that.repos) &&
213                members.equals(that.members) &&
214                tokens.equals(that.tokens) &&
215                creation.equals(that.creation) &&
216                Objects.equals(removal, that.removal);
217     }
218 
219     @Override
220     public int hashCode() {
221         return Objects.hash(name, repos, members, tokens, creation, removal);
222     }
223 
224     @Override
225     public String toString() {
226         return MoreObjects.toStringHelper(this)
227                           .add("name", name())
228                           .add("repos", repos())
229                           .add("members", members())
230                           .add("tokens", tokens())
231                           .add("creation", creation())
232                           .add("removal", removal())
233                           .toString();
234     }
235 
236     /**
237      * Returns a new {@link ProjectMetadata} without the Dogma repository.
238      */
239     public ProjectMetadata withoutDogmaRepo() {
240         if (!repos().containsKey(Project.REPO_DOGMA)) {
241             return this;
242         }
243         final Map<String, RepositoryMetadata> filtered =
244                 repos().entrySet().stream().filter(entry -> !Project.REPO_DOGMA.equals(entry.getKey()))
245                        .collect(toImmutableMap(Entry::getKey, Entry::getValue));
246         return new ProjectMetadata(name(),
247                                    filtered,
248                                    members(),
249                                    tokens(),
250                                    creation(),
251                                    removal());
252     }
253 }