001/* 002 * Copyright (c) 2023 Chris K Wensel <[email protected]>. All Rights Reserved. 003 * 004 * This Source Code Form is subject to the terms of the Mozilla Public 005 * License, v. 2.0. If a copy of the MPL was not distributed with this 006 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 007 */ 008 009package clusterless.commons.naming; 010 011import java.util.HashMap; 012import java.util.Map; 013import java.util.Objects; 014import java.util.Optional; 015 016/** 017 * Creates an identifier to use as an export/import key for provider output/export values. 018 * <p/> 019 * <pre> 020 * aws:qualifier:[stage:]scopeName:scopeVersion:resourceNS:resourceType:resourceName 021 * </pre> 022 * <p/> 023 * Where the qualifier represents the type of value being exported/imported: 024 * - id 025 * - name 026 * - arn 027 * <p/> 028 * Where stage is the stage of the deployment, such as dev, test, prod, etc. It is optional. 029 * <p/> 030 * scopeName and scopeVersion are analogous to project names and versions. 031 * <p/> 032 * Resources are identified by a namespace, type, and name. 033 * <p/> 034 * 035 * <pre> 036 * ref:aws:id:project-a:20230101:core:compute:spot 037 * ref:aws:id:dev:project-a:20230101:core:compute:spot 038 * </pre> 039 */ 040public final class Ref { 041 public static Ref ref() { 042 return new Ref(); 043 } 044 045 public static Ref idRef() { 046 return new Ref().withQualifier(Qualifier.Id); 047 } 048 049 public static Ref arnRef() { 050 return new Ref().withQualifier(Qualifier.Arn); 051 } 052 053 public static Ref nameRef() { 054 return new Ref().withQualifier(Qualifier.Name); 055 } 056 057 public static boolean isRef(String value) { 058 return value != null && value.startsWith("ref:"); 059 } 060 061 public static Optional<Qualifier> qualifier(String value) { 062 if (!isRef(value)) { 063 return Optional.empty(); 064 } 065 066 return Qualifier.lookup(value.split(":")[2]); 067 } 068 069 public static Optional<String> provider(String value) { 070 if (!isRef(value)) { 071 return Optional.empty(); 072 } 073 074 return Optional.ofNullable(value.split(":")[1]); 075 } 076 077 final Fixed provider; 078 final Qualifier qualifier; 079 final Stage stage; 080 final Fixed scope; 081 final Version scopeVersion; 082 final Fixed resourceNs; 083 final Fixed resourceType; 084 final Fixed resourceName; 085 086 public Ref() { 087 provider = Fixed.fixedNull(); 088 qualifier = null; 089 stage = Stage.nullStage(); 090 scope = Fixed.fixedNull(); 091 scopeVersion = Version.versionNull(); 092 resourceNs = Fixed.fixedNull(); 093 resourceType = Fixed.fixedNull(); 094 resourceName = Fixed.fixedNull(); 095 } 096 097 private Ref(Fixed provider, Qualifier qualifier, Stage stage, Fixed scope, Version scopeVersion, Fixed resourceNs, Fixed resourceType, Fixed resourceName) { 098 this.provider = provider; 099 this.qualifier = qualifier; 100 this.stage = stage == null ? Stage.nullStage() : stage.asLower(); 101 this.scope = scope; 102 this.scopeVersion = scopeVersion; 103 this.resourceNs = resourceNs; 104 this.resourceType = resourceType; 105 this.resourceName = resourceName; 106 } 107 108 public Ref withProvider(String provider) { 109 Objects.requireNonNull(provider); 110 return withProvider(Label.of(provider)); 111 } 112 113 public Ref withProvider(Label provider) { 114 Objects.requireNonNull(provider); 115 return withProvider(Fixed.of(provider.lowerHyphen())); 116 } 117 118 public Ref withProvider(Fixed provider) { 119 Label.requireNonEmpty(provider); 120 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 121 } 122 123 public Ref withStage(Stage stage) { 124 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 125 } 126 127 public Ref withScope(String scope) { 128 return withScope(Label.of(scope)); 129 } 130 131 public Ref withScope(Label scope) { 132 return withScope(Fixed.of(scope.lowerHyphen())); 133 } 134 135 public Ref withScope(Fixed scope) { 136 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 137 } 138 139 public Ref withScopeVersion(String scopeVersion) { 140 return withScopeVersion(Version.of(scopeVersion)); 141 } 142 143 public Ref withScopeVersion(Fixed scopeVersion) { 144 return withScopeVersion(Version.of(scopeVersion.lowerHyphen())); 145 } 146 147 public Ref withScopeVersion(Version scopeVersion) { 148 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 149 } 150 151 public Ref withResourceNs(String resourceNs) { 152 return withResourceNs(Label.of(resourceNs)); 153 } 154 155 public Ref withResourceNs(Label resourceNs) { 156 return withResourceNs(Fixed.of(resourceNs.lowerHyphen())); 157 } 158 159 public Ref withResourceNs(Fixed resourceNs) { 160 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 161 } 162 163 public Ref withResourceType(String resourceType) { 164 return withResourceType(Label.of(resourceType)); 165 } 166 167 public Ref withResourceType(Label resourceType) { 168 return withResourceType(Fixed.of(resourceType.lowerHyphen())); 169 } 170 171 public Ref withResourceType(Fixed resourceType) { 172 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 173 } 174 175 public Ref withResourceName(String resourceName) { 176 return withResourceName(Label.of(resourceName)); 177 } 178 179 public Ref withResourceName(Label resourceName) { 180 return withResourceName(Fixed.of(resourceName.lowerHyphen())); 181 } 182 183 public Ref withResourceName(Fixed resourceName) { 184 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 185 } 186 187 public Ref withQualifier(Qualifier qualifier) { 188 return new Ref(provider, qualifier, stage, scope, scopeVersion, resourceNs, resourceType, resourceName); 189 } 190 191 public Label provider() { 192 return provider; 193 } 194 195 public Stage stage() { 196 return stage; 197 } 198 199 public Fixed scope() { 200 return scope; 201 } 202 203 public Fixed scopeVersion() { 204 return scopeVersion; 205 } 206 207 public Fixed resourceNs() { 208 return resourceNs; 209 } 210 211 public Fixed resourceType() { 212 return resourceType; 213 } 214 215 public Fixed resourceName() { 216 return resourceName; 217 } 218 219 public Qualifier qualifier() { 220 return qualifier; 221 } 222 223 public Label resourceLabel() { 224 requireNonNull(resourceNs, "resourceNs required"); 225 requireNonNull(resourceType, "resourceType required"); 226 requireNonNull(resourceName, "resourceName required"); 227 228 return Label.NULL 229 .with(resourceNs) 230 .with(Label.of(resourceType.value())) 231 .with(resourceName); 232 } 233 234 public Label label() { 235 requireNonNull(provider, "provider required"); 236 requireNonNull(qualifier, "qualifier required"); 237 requireNonNull(scope, "scope required"); 238 requireNonNull(scopeVersion, "scopeVersion required"); 239 requireNonNull(resourceNs, "resourceNs required"); 240 requireNonNull(resourceType, "resourceType required"); 241 requireNonNull(resourceName, "resourceName required"); 242 243 return Label.of("ref") 244 .with(provider) 245 .with(qualifier) 246 .with(stage) 247 .with(scope) 248 .with(scopeVersion) 249 .with(resourceNs) 250 .with(resourceType) 251 .with(resourceName); 252 } 253 254 private static void requireNonNull(Label label, String message) { 255 Objects.requireNonNull(label, message); 256 257 if (label.isNull()) { 258 throw new NullPointerException(message); 259 } 260 } 261 262 263 public String exportName() { 264 return label().lowerColonPath(); 265 } 266 267 @Override 268 public String toString() { 269 return Label.of("ref") 270 .with(provider) 271 .with(qualifier) 272 .with(stage) 273 .with(scope) 274 .with(scopeVersion) 275 .with(resourceNs) 276 .with(resourceType) 277 .with(resourceName).lowerColonPath(); 278 } 279 280 @Override 281 public boolean equals(Object o) { 282 if (this == o) return true; 283 if (o == null || getClass() != o.getClass()) return false; 284 Ref ref = (Ref) o; 285 return Objects.equals(provider, ref.provider) && Objects.equals(stage, ref.stage) && Objects.equals(scope, ref.scope) && Objects.equals(scopeVersion, ref.scopeVersion) && Objects.equals(resourceNs, ref.resourceNs) && Objects.equals(resourceType, ref.resourceType) && Objects.equals(resourceName, ref.resourceName) && qualifier == ref.qualifier; 286 } 287 288 @Override 289 public int hashCode() { 290 return Objects.hash(provider, stage, scope, scopeVersion, resourceNs, resourceType, resourceName, qualifier); 291 } 292 293 public enum Qualifier implements Label.EnumLabel { 294 Name, 295 Id, 296 Arn; 297 298 static final Map<String, Qualifier> map = new HashMap<>() { 299 { 300 put(Name.lowerHyphen(), Name); 301 put(Id.lowerHyphen(), Id); 302 put(Arn.lowerHyphen(), Arn); 303 } 304 }; 305 306 public static Optional<Qualifier> lookup(String value) { 307 if (value == null) { 308 return null; 309 } 310 311 return Optional.ofNullable(map.get(value.toLowerCase())); 312 } 313 } 314}