1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.api.generator.gapic.model;
16 
17 import com.google.api.generator.engine.ast.TypeNode;
18 import com.google.auto.value.AutoValue;
19 import com.google.common.base.Splitter;
20 import com.google.common.base.Strings;
21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Iterables;
23 import java.util.List;
24 import javax.annotation.Nullable;
25 
26 @AutoValue
27 public abstract class Service {
name()28   public abstract String name();
29 
defaultHost()30   public abstract String defaultHost();
31 
oauthScopes()32   public abstract ImmutableList<String> oauthScopes();
33 
pakkage()34   public abstract String pakkage();
35 
protoPakkage()36   public abstract String protoPakkage();
37 
38   // For compatibility with other protoc-plugin code generators, e.g. gRPC.
originalJavaPackage()39   public abstract String originalJavaPackage();
40 
41   // New Java class name as defined in gapic.yaml's language settings.
overriddenName()42   public abstract String overriddenName();
43 
isDeprecated()44   public abstract boolean isDeprecated();
45 
methods()46   public abstract ImmutableList<Method> methods();
47 
48   @Nullable
description()49   public abstract String description();
50 
hasDescription()51   public boolean hasDescription() {
52     return !Strings.isNullOrEmpty(description());
53   }
54 
apiShortName()55   public String apiShortName() {
56     if (!Strings.isNullOrEmpty(defaultHost())) {
57       return parseApiShortName(defaultHost());
58     }
59     return "";
60   }
61 
apiVersion()62   public String apiVersion() {
63     if (!Strings.isNullOrEmpty(protoPakkage())) {
64       return parseApiVersion(protoPakkage());
65     }
66     return "";
67   }
68 
operationPollingMethod()69   public Method operationPollingMethod() {
70     for (Method method : methods()) {
71       if (method.isOperationPollingMethod()) {
72         return method;
73       }
74     }
75     return null;
76   }
77 
operationServiceStubType()78   public TypeNode operationServiceStubType() {
79     for (Method method : methods()) {
80       if (method.hasLro() && method.lro().operationServiceStubType() != null) {
81         // All methods within the same service must have the same operationServiceTypeName if
82         // present
83         return method.lro().operationServiceStubType();
84       }
85     }
86     return null;
87   }
88 
operationType()89   public TypeNode operationType() {
90     for (Method method : methods()) {
91       if (method.hasLro() && method.lro().operationServiceStubType() != null) {
92         return method.outputType();
93       }
94     }
95     return null;
96   }
97 
hasLroMethods()98   public boolean hasLroMethods() {
99     for (Method method : methods()) {
100       if (method.hasLro()) {
101         return true;
102       }
103     }
104     return false;
105   }
106 
hasStandardLroMethods()107   public boolean hasStandardLroMethods() {
108     for (Method method : methods()) {
109       if (method.hasLro() && method.lro().operationServiceStubType() == null) {
110         return true;
111       }
112     }
113     return false;
114   }
115 
116   /**
117    * Wrapper for hasAnyEnabledMethodsForTransport(Transport). Some invocations are called with
118    * `gRPC` which can't match with the correct Transport (GRPC)
119    *
120    * @param transportName String transport value
121    * @return boolean if service contains any enabled methods for a transport
122    */
hasAnyEnabledMethodsForTransport(String transportName)123   public boolean hasAnyEnabledMethodsForTransport(String transportName) {
124     return hasAnyEnabledMethodsForTransport(Transport.parse(transportName));
125   }
126 
127   /**
128    * Determines if a Service contains any methods that are both eligible and enabled for the
129    * Transport. GRPC+REST Transport is not supported as each transport's sub composers will invoke
130    * this method the specific transport (GRPC or REST)
131    *
132    * @param transport Expects either GRPC or REST Transport
133    * @return boolean if service contains any enabled methods for a transport
134    */
hasAnyEnabledMethodsForTransport(Transport transport)135   public boolean hasAnyEnabledMethodsForTransport(Transport transport) {
136     if (transport == Transport.GRPC_REST) {
137       throw new IllegalArgumentException(
138           String.format("Invalid Transport: %s. Expecting GRPC or REST", transport.name()));
139     }
140     return methods().stream().anyMatch(x -> x.isSupportedByTransport(transport));
141   }
142 
toBuilder()143   public abstract Builder toBuilder();
144 
builder()145   public static Builder builder() {
146     return new AutoValue_Service.Builder().setMethods(ImmutableList.of()).setIsDeprecated(false);
147   }
148 
149   @AutoValue.Builder
150   public abstract static class Builder {
setName(String name)151     public abstract Builder setName(String name);
152 
setOverriddenName(String overriddenName)153     public abstract Builder setOverriddenName(String overriddenName);
154 
setDefaultHost(String defaultHost)155     public abstract Builder setDefaultHost(String defaultHost);
156 
setOauthScopes(List<String> oauthScopes)157     public abstract Builder setOauthScopes(List<String> oauthScopes);
158 
setPakkage(String pakkage)159     public abstract Builder setPakkage(String pakkage);
160 
setProtoPakkage(String pakkage)161     public abstract Builder setProtoPakkage(String pakkage);
162 
setOriginalJavaPackage(String originalJavaPackage)163     public abstract Builder setOriginalJavaPackage(String originalJavaPackage);
164 
setIsDeprecated(boolean isDeprecated)165     public abstract Builder setIsDeprecated(boolean isDeprecated);
166 
setMethods(List<Method> methods)167     public abstract Builder setMethods(List<Method> methods);
168 
setDescription(String description)169     public abstract Builder setDescription(String description);
170 
build()171     public abstract Service build();
172   }
173 
parseApiVersion(String protoPackage)174   private static String parseApiVersion(String protoPackage) {
175     //  parse protoPackage for apiVersion
176     String[] pakkage = protoPackage.split("\\.");
177     String apiVersion;
178     //  e.g. v1, v2, v1beta1
179     if (pakkage[pakkage.length - 1].matches("v[0-9].*")) {
180       apiVersion = pakkage[pakkage.length - 1];
181     } else {
182       apiVersion = "";
183     }
184     return apiVersion;
185   }
186 
187   // Parse defaultHost for apiShortName for the RegionTag. Need to account for regional default
188   // endpoints like
189   // "us-east1-pubsub.googleapis.com".
parseApiShortName(String defaultHost)190   private static String parseApiShortName(String defaultHost) {
191     // If the defaultHost is of the format "**.googleapis.com", take the name before the first
192     // period.
193     String apiShortName = Iterables.getFirst(Splitter.on(".").split(defaultHost), defaultHost);
194     // If the defaultHost is of the format "**-**-**.googleapis.com", take the section before the
195     // first period and after the last dash to follow CSharp's implementation here:
196     // https://github.com/googleapis/gapic-generator-csharp/blob/main/Google.Api.Generator/Generation/ServiceDetails.cs#L70
197     apiShortName = Iterables.getLast(Splitter.on("-").split(apiShortName), defaultHost);
198     // `iam-meta-api` service is an exceptional case and is handled as a one-off
199     if (defaultHost.contains("iam-meta-api")) {
200       apiShortName = "iam";
201     }
202     return apiShortName;
203   }
204 }
205