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