• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cobalt.registry;
18 
19 import android.annotation.Nullable;
20 
21 import com.google.cobalt.CobaltRegistry;
22 import com.google.cobalt.CustomerConfig;
23 import com.google.cobalt.MetricDefinition;
24 import com.google.cobalt.ProjectConfig;
25 import com.google.cobalt.ReportDefinition;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ImmutableMap;
28 import com.google.common.collect.ImmutableSet;
29 
30 import java.util.HashMap;
31 import java.util.Map;
32 
33 /** Merges two registries to create a new registry with their combined content. */
34 public final class RegistryMerger {
35 
36     /**
37      * Merges two Cobalt registries by the contents of the base registry and merging it with the
38      * content of the merge in registry.
39      *
40      * <p>The only content added to the base registry is valid reports from the merge in registry,
41      * if they're not already in the base registry.
42      *
43      * <p>Additionally, customer, project, metrics, reports may be deleted from the base registry if
44      * they're deleted in the merge in registry.
45      */
mergeRegistries( CobaltRegistry baseRegistry, CobaltRegistry mergeInRegistry)46     public static CobaltRegistry mergeRegistries(
47             CobaltRegistry baseRegistry, CobaltRegistry mergeInRegistry) {
48         ImmutableList.Builder<CustomerConfig> mergedCustomers = ImmutableList.builder();
49         ImmutableSet<Integer> deletedCustomerIds =
50                 ImmutableSet.<Integer>builder()
51                         .addAll(baseRegistry.getDeletedCustomerIdsList())
52                         .addAll(mergeInRegistry.getDeletedCustomerIdsList())
53                         .build();
54 
55         ImmutableMap<Integer, CustomerConfig> mergeInCustomers = getCustomers(mergeInRegistry);
56         for (CustomerConfig baseCustomer : baseRegistry.getCustomersList()) {
57             if (deletedCustomerIds.contains(baseCustomer.getCustomerId())) {
58                 continue;
59             }
60 
61             mergedCustomers.add(
62                     mergeCustomers(
63                             baseCustomer, mergeInCustomers.get(baseCustomer.getCustomerId())));
64         }
65 
66         return baseRegistry.toBuilder()
67                 .clearCustomers()
68                 .addAllCustomers(mergedCustomers.build())
69                 .clearDeletedCustomerIds()
70                 .addAllDeletedCustomerIds(deletedCustomerIds)
71                 .build();
72     }
73 
mergeCustomers( CustomerConfig baseCustomer, @Nullable CustomerConfig mergeInCustomer)74     private static CustomerConfig mergeCustomers(
75             CustomerConfig baseCustomer, @Nullable CustomerConfig mergeInCustomer) {
76         if (mergeInCustomer == null) {
77             return baseCustomer;
78         }
79 
80         ImmutableList.Builder<ProjectConfig> mergedProjects = ImmutableList.builder();
81         ImmutableSet<Integer> deletedProjectIds =
82                 ImmutableSet.<Integer>builder()
83                         .addAll(baseCustomer.getDeletedProjectIdsList())
84                         .addAll(mergeInCustomer.getDeletedProjectIdsList())
85                         .build();
86 
87         ImmutableMap<Integer, ProjectConfig> mergeInProjects = getProjects(mergeInCustomer);
88         for (ProjectConfig baseProject : baseCustomer.getProjectsList()) {
89             if (deletedProjectIds.contains(baseProject.getProjectId())) {
90                 continue;
91             }
92 
93             mergedProjects.add(
94                     mergeProjects(baseProject, mergeInProjects.get(baseProject.getProjectId())));
95         }
96 
97         return baseCustomer.toBuilder()
98                 .clearProjects()
99                 .addAllProjects(mergedProjects.build())
100                 .clearDeletedProjectIds()
101                 .addAllDeletedProjectIds(deletedProjectIds)
102                 .build();
103     }
104 
mergeProjects( ProjectConfig baseProject, @Nullable ProjectConfig mergeInProject)105     private static ProjectConfig mergeProjects(
106             ProjectConfig baseProject, @Nullable ProjectConfig mergeInProject) {
107         if (mergeInProject == null) {
108             return baseProject;
109         }
110 
111         ImmutableList.Builder<MetricDefinition> mergedMetrics = ImmutableList.builder();
112         ImmutableSet<Integer> deletedMetricIds =
113                 ImmutableSet.<Integer>builder()
114                         .addAll(baseProject.getDeletedMetricIdsList())
115                         .addAll(mergeInProject.getDeletedMetricIdsList())
116                         .build();
117 
118         ImmutableMap<Integer, MetricDefinition> mergeInMetrics = getMetrics(mergeInProject);
119         for (MetricDefinition baseMetric : baseProject.getMetricsList()) {
120             if (deletedMetricIds.contains(baseMetric.getId())) {
121                 continue;
122             }
123 
124             mergedMetrics.add(mergeMetrics(baseMetric, mergeInMetrics.get(baseMetric.getId())));
125         }
126 
127         return baseProject.toBuilder()
128                 .clearMetrics()
129                 .addAllMetrics(mergedMetrics.build())
130                 .clearDeletedMetricIds()
131                 .addAllDeletedMetricIds(deletedMetricIds)
132                 .build();
133     }
134 
mergeMetrics( MetricDefinition baseMetric, @Nullable MetricDefinition mergeInMetric)135     private static MetricDefinition mergeMetrics(
136             MetricDefinition baseMetric, @Nullable MetricDefinition mergeInMetric) {
137         if (mergeInMetric == null) {
138             return baseMetric;
139         }
140 
141         Map<Integer, ReportDefinition> mergedReports = new HashMap<>();
142         ImmutableSet<Integer> deletedReportIds =
143                 ImmutableSet.<Integer>builder()
144                         .addAll(baseMetric.getDeletedReportIdsList())
145                         .addAll(mergeInMetric.getDeletedReportIdsList())
146                         .build();
147 
148         ImmutableMap<Integer, ReportDefinition> baseReports = getReports(baseMetric);
149         ImmutableMap<Integer, ReportDefinition> mergeInReports = getReports(mergeInMetric);
150 
151         for (Map.Entry<Integer, ReportDefinition> report : baseReports.entrySet()) {
152             if (deletedReportIds.contains(report.getKey())) {
153                 continue;
154             }
155 
156             mergedReports.put(report.getKey(), report.getValue());
157         }
158         for (Map.Entry<Integer, ReportDefinition> report : mergeInReports.entrySet()) {
159             if (deletedReportIds.contains(report.getKey())) {
160                 continue;
161             }
162             if (mergedReports.containsKey(report.getKey())) {
163                 continue;
164             }
165             if (!RegistryValidator.isValid(baseMetric, report.getValue())) {
166                 // Invalid reports from the registry being merged in are considered deleted for
167                 // safety.
168                 deletedReportIds =
169                         ImmutableSet.<Integer>builderWithExpectedSize(deletedReportIds.size() + 1)
170                                 .addAll(deletedReportIds)
171                                 .add(report.getKey())
172                                 .build();
173                 continue;
174             }
175 
176             mergedReports.put(report.getKey(), report.getValue());
177         }
178 
179         return baseMetric.toBuilder()
180                 .clearReports()
181                 .addAllReports(mergedReports.values())
182                 .clearDeletedReportIds()
183                 .addAllDeletedReportIds(deletedReportIds)
184                 .build();
185     }
186 
getCustomers(CobaltRegistry registry)187     private static ImmutableMap<Integer, CustomerConfig> getCustomers(CobaltRegistry registry) {
188         ImmutableMap.Builder<Integer, CustomerConfig> customers = ImmutableMap.builder();
189         for (CustomerConfig customer : registry.getCustomersList()) {
190             customers.put(customer.getCustomerId(), customer);
191         }
192         return customers.build();
193     }
194 
getProjects(CustomerConfig customer)195     private static ImmutableMap<Integer, ProjectConfig> getProjects(CustomerConfig customer) {
196         ImmutableMap.Builder<Integer, ProjectConfig> projects = ImmutableMap.builder();
197         for (ProjectConfig project : customer.getProjectsList()) {
198             projects.put(project.getProjectId(), project);
199         }
200         return projects.build();
201     }
202 
getMetrics(ProjectConfig project)203     private static ImmutableMap<Integer, MetricDefinition> getMetrics(ProjectConfig project) {
204         ImmutableMap.Builder<Integer, MetricDefinition> metrics = ImmutableMap.builder();
205         for (MetricDefinition metric : project.getMetricsList()) {
206             metrics.put(metric.getId(), metric);
207         }
208         return metrics.build();
209     }
210 
getReports(MetricDefinition metric)211     private static ImmutableMap<Integer, ReportDefinition> getReports(MetricDefinition metric) {
212         ImmutableMap.Builder<Integer, ReportDefinition> reports = ImmutableMap.builder();
213         for (ReportDefinition report : metric.getReportsList()) {
214             reports.put(report.getId(), report);
215         }
216         return reports.build();
217     }
218 
RegistryMerger()219     private RegistryMerger() {}
220 }
221