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      public static final ProjectMetadata DOGMA_PROJECT_METADATA =
48              new ProjectMetadata("dogma",
49                                  ImmutableMap.of(),
50                                  ImmutableMap.of(),
51                                  ImmutableMap.of(),
52                                  new UserAndTimestamp(User.SYSTEM.id()),
53                                  null);
54  
55      /**
56       * A project name.
57       */
58      private final String name;
59  
60      /**
61       * Repositories of this project.
62       */
63      private final Map<String, RepositoryMetadata> repos;
64  
65      /**
66       * Members of this project.
67       */
68      private final Map<String, Member> members;
69  
70      /**
71       * Tokens which belong to this project.
72       */
73      private final Map<String, TokenRegistration> tokens;
74  
75      /**
76       * Specifies when this project is created by whom.
77       */
78      private final UserAndTimestamp creation;
79  
80      /**
81       * Specifies when this project is removed by whom.
82       */
83      @Nullable
84      private final UserAndTimestamp removal;
85  
86      /**
87       * Creates a new instance.
88       */
89      @JsonCreator
90      public ProjectMetadata(@JsonProperty("name") String name,
91                             @JsonProperty("repos") Map<String, RepositoryMetadata> repos,
92                             @JsonProperty("members") Map<String, Member> members,
93                             @JsonProperty("tokens") Map<String, TokenRegistration> tokens,
94                             @JsonProperty("creation") UserAndTimestamp creation,
95                             @JsonProperty("removal") @Nullable UserAndTimestamp removal) {
96          this.name = requireNonNull(name, "name");
97          this.repos = ImmutableMap.copyOf(requireNonNull(repos, "repos"));
98          this.members = ImmutableMap.copyOf(requireNonNull(members, "members"));
99          this.tokens = ImmutableMap.copyOf(requireNonNull(tokens, "tokens"));
100         this.creation = requireNonNull(creation, "creation");
101         this.removal = removal;
102     }
103 
104     @Override
105     public String id() {
106         return name;
107     }
108 
109     /**
110      * Returns the project name.
111      */
112     @JsonProperty
113     public String name() {
114         return name;
115     }
116 
117     /**
118      * Returns the metadata of the repositories in this project.
119      */
120     @JsonProperty
121     public Map<String, RepositoryMetadata> repos() {
122         return repos;
123     }
124 
125     /**
126      * Returns the {@link Member}s of this project.
127      */
128     @JsonProperty
129     public Map<String, Member> members() {
130         return members;
131     }
132 
133     /**
134      * Returns the {@link TokenRegistration}s of this project.
135      */
136     @JsonProperty
137     public Map<String, TokenRegistration> tokens() {
138         return tokens;
139     }
140 
141     /**
142      * Returns who created this project when.
143      */
144     @JsonProperty
145     public UserAndTimestamp creation() {
146         return creation;
147     }
148 
149     /**
150      * Returns who removed this project when.
151      */
152     @Nullable
153     @JsonProperty
154     public UserAndTimestamp removal() {
155         return removal;
156     }
157 
158     /**
159      * Returns the {@link RepositoryMetadata} of the specified repository in this project.
160      */
161     public RepositoryMetadata repo(String repoName) {
162         final RepositoryMetadata repositoryMetadata =
163                 repos.get(requireNonNull(repoName, "repoName"));
164         if (repositoryMetadata != null) {
165             return repositoryMetadata;
166         }
167         throw RepositoryNotFoundException.of(name, repoName);
168     }
169 
170     /**
171      * Returns the {@link Member} of the specified ID in this project.
172      */
173     public Member member(String memberId) {
174         final Member member = memberOrDefault(memberId, null);
175         if (member != null) {
176             return member;
177         }
178         throw new MemberNotFoundException(memberId, name());
179     }
180 
181     /**
182      * Returns the {@link Member} of the specified ID in this project.
183      * {@code defaultMember} is returned if there is no such member.
184      */
185     @Nullable
186     public Member memberOrDefault(String memberId, @Nullable Member defaultMember) {
187         final Member member = members.get(requireNonNull(memberId, "memberId"));
188         if (member != null) {
189             return member;
190         }
191         return defaultMember;
192     }
193 
194     /**
195      * Returns the {@link TokenRegistration} of the specified application ID in this project.
196      */
197     @Nullable
198     public TokenRegistration tokenOrDefault(String appId, @Nullable TokenRegistration defaultToken) {
199         final TokenRegistration token = tokens.get(requireNonNull(appId, "appId"));
200         if (token != null) {
201             return token;
202         }
203         return defaultToken;
204     }
205 
206     @Override
207     public int weight() {
208         int weight = name().length();
209         for (RepositoryMetadata repo : repos.values()) {
210             weight += repo.weight();
211         }
212         for (Member member : members.values()) {
213             weight += member.weight();
214         }
215         for (TokenRegistration token : tokens.values()) {
216             weight += token.weight();
217         }
218 
219         return weight;
220     }
221 
222     @Override
223     public boolean equals(Object o) {
224         if (this == o) {
225             return true;
226         }
227         if (!(o instanceof ProjectMetadata)) {
228             return false;
229         }
230         final ProjectMetadata that = (ProjectMetadata) o;
231         return name.equals(that.name) &&
232                repos.equals(that.repos) &&
233                members.equals(that.members) &&
234                tokens.equals(that.tokens) &&
235                creation.equals(that.creation) &&
236                Objects.equals(removal, that.removal);
237     }
238 
239     @Override
240     public int hashCode() {
241         return Objects.hash(name, repos, members, tokens, creation, removal);
242     }
243 
244     @Override
245     public String toString() {
246         return MoreObjects.toStringHelper(this)
247                           .add("name", name())
248                           .add("repos", repos())
249                           .add("members", members())
250                           .add("tokens", tokens())
251                           .add("creation", creation())
252                           .add("removal", removal())
253                           .toString();
254     }
255 
256     /**
257      * Returns a new {@link ProjectMetadata} without the Dogma repository.
258      */
259     public ProjectMetadata withoutDogmaRepo() {
260         if (!repos().containsKey(Project.REPO_DOGMA)) {
261             return this;
262         }
263         final Map<String, RepositoryMetadata> filtered =
264                 repos().entrySet().stream().filter(entry -> !Project.REPO_DOGMA.equals(entry.getKey()))
265                        .collect(toImmutableMap(Entry::getKey, Entry::getValue));
266         return new ProjectMetadata(name(),
267                                    filtered,
268                                    members(),
269                                    tokens(),
270                                    creation(),
271                                    removal());
272     }
273 }