diff --git a/build.gradle b/build.gradle index e995ea2455..d77e4c602c 100644 --- a/build.gradle +++ b/build.gradle @@ -452,6 +452,16 @@ task runDiscoBatch(type: JavaExec) { } } +// Task to run DiscoGapicGeneratorTool +// ============================= +task runDiscoGapicGen(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + main = 'com.google.api.codegen.DiscoGapicGeneratorTool' + if (project.hasProperty('clargs')) { + args = clargs.split(',').toList() + } +} + task verifyLicense << { def licenseText = new File(rootProject.rootDir, 'license-header-javadoc.txt').text def srcFiles = [] diff --git a/src/main/java/com/google/api/codegen/DiscoGapicGeneratorApi.java b/src/main/java/com/google/api/codegen/DiscoGapicGeneratorApi.java new file mode 100644 index 0000000000..4b10aaf202 --- /dev/null +++ b/src/main/java/com/google/api/codegen/DiscoGapicGeneratorApi.java @@ -0,0 +1,185 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.config.PackageMetadataConfig; +import com.google.api.codegen.discogapic.DiscoGapicProvider; +import com.google.api.codegen.discogapic.DiscoGapicProviderFactory; +import com.google.api.codegen.discovery.DiscoveryNode; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.gapic.GapicGeneratorConfig; +import com.google.api.codegen.util.ClassInstantiator; +import com.google.api.tools.framework.model.DiagCollector; +import com.google.api.tools.framework.model.SimpleDiagCollector; +import com.google.api.tools.framework.snippet.Doc; +import com.google.api.tools.framework.tools.ToolOptions; +import com.google.api.tools.framework.tools.ToolOptions.Option; +import com.google.api.tools.framework.tools.ToolUtil; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import com.google.inject.TypeLiteral; +import com.google.protobuf.Message; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DiscoGapicGeneratorApi { + public static final Option DISCOVERY_DOC = + ToolOptions.createOption( + String.class, + "discovery_doc", + "The Discovery doc representing the service description.", + ""); + + public static final Option OUTPUT_FILE = + ToolOptions.createOption( + String.class, + "output_file", + "The name of the output file or folder to put generated code.", + ""); + + public static final Option> GENERATOR_CONFIG_FILES = + ToolOptions.createOption( + new TypeLiteral>() {}, + "config_files", + "The list of YAML configuration files for the code generator.", + ImmutableList.of()); + + public static final Option PACKAGE_CONFIG_FILE = + ToolOptions.createOption( + String.class, "package_config", "The package metadata configuration.", ""); + + public static final Option> ENABLED_ARTIFACTS = + ToolOptions.createOption( + new TypeLiteral>() {}, + "enabled_artifacts", + "The artifacts to be generated by the code generator.", + ImmutableList.of()); + + private final ToolOptions options; + + /** Constructs a code generator api based on given options. */ + public DiscoGapicGeneratorApi(ToolOptions options) { + this.options = options; + } + + public void run() throws Exception { + + String discoveryDocPath = options.get(DISCOVERY_DOC); + if (!new File(discoveryDocPath).exists()) { + throw new IOException("File not found: " + discoveryDocPath); + } + Reader reader = new InputStreamReader(new FileInputStream(new File(discoveryDocPath))); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode root = mapper.readTree(reader); + + Document document = Document.from(new DiscoveryNode(root)); + + // Read the YAML config and convert it to proto. + List configFileNames = options.get(GENERATOR_CONFIG_FILES); + if (configFileNames.size() == 0) { + System.err.println(String.format("--%s must be provided", GENERATOR_CONFIG_FILES.name())); + return; + } + + ConfigProto configProto = loadConfigFromFiles(configFileNames); + if (configProto == null) { + return; + } + + PackageMetadataConfig packageConfig = null; + if (!Strings.isNullOrEmpty(options.get(PACKAGE_CONFIG_FILE))) { + String contents = + new String( + Files.readAllBytes(Paths.get(options.get(PACKAGE_CONFIG_FILE))), + StandardCharsets.UTF_8); + packageConfig = PackageMetadataConfig.createFromString(contents); + } + GeneratorProto generator = configProto.getGenerator(); + GapicProductConfig productConfig = GapicProductConfig.create(document, configProto); + + String factory = generator.getFactory(); + String id = generator.getId(); + + DiscoGapicProviderFactory providerFactory = createProviderFactory(factory); + GapicGeneratorConfig generatorConfig = + GapicGeneratorConfig.newBuilder() + .id(id) + .enabledArtifacts(options.get(ENABLED_ARTIFACTS)) + .build(); + + List providers = + providerFactory.create(document, productConfig, generatorConfig, packageConfig); + String outputFile = options.get(OUTPUT_FILE); + Map outputFiles = Maps.newHashMap(); + for (DiscoGapicProvider provider : providers) { + outputFiles.putAll(provider.generate()); + } + ToolUtil.writeFiles(outputFiles, outputFile); + } + + private static DiscoGapicProviderFactory createProviderFactory(String factory) { + @SuppressWarnings("unchecked") + DiscoGapicProviderFactory provider = + ClassInstantiator.createClass( + factory, + DiscoGapicProviderFactory.class, + new Class[] {}, + new Object[] {}, + "generator", + new ClassInstantiator.ErrorReporter() { + @Override + public void error(String message, Object... args) { + System.err.printf(message, args); + } + }); + return provider; + } + + private List pathsToFiles(List configFileNames) { + List files = new ArrayList<>(); + + for (String configFileName : configFileNames) { + files.add(new File(configFileName)); + } + + return files; + } + + private ConfigProto loadConfigFromFiles(List configFileNames) { + List configFiles = pathsToFiles(configFileNames); + DiagCollector diagCollector = new SimpleDiagCollector(); + ImmutableMap supportedConfigTypes = + ImmutableMap.of( + ConfigProto.getDescriptor().getFullName(), ConfigProto.getDefaultInstance()); + ConfigProto configProto = + (ConfigProto) MultiYamlReader.read(diagCollector, configFiles, supportedConfigTypes); + return configProto; + } +} diff --git a/src/main/java/com/google/api/codegen/DiscoGapicGeneratorTool.java b/src/main/java/com/google/api/codegen/DiscoGapicGeneratorTool.java new file mode 100644 index 0000000000..7dc891ce50 --- /dev/null +++ b/src/main/java/com/google/api/codegen/DiscoGapicGeneratorTool.java @@ -0,0 +1,120 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen; + +import com.google.api.tools.framework.tools.ToolOptions; +import com.google.common.collect.Lists; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +public class DiscoGapicGeneratorTool { + public static void main(String[] args) throws Exception { + Options options = new Options(); + options.addOption("h", "help", false, "show usage"); + options.addOption( + Option.builder() + .longOpt("discovery_doc") + .desc("The Discovery doc representing the service description.") + .hasArg() + .argName("DISCOVERY-DOC") + .required(true) + .build()); + // TODO add this option back + // options.addOption( + // Option.builder() + // .longOpt("service_yaml") + // .desc("The service YAML configuration file or files.") + // .hasArg() + // .argName("SERVICE-YAML") + // .required(true) + // .build()); + options.addOption( + Option.builder() + .longOpt("gapic_yaml") + .desc("The GAPIC YAML configuration file or files.") + .hasArg() + .argName("GAPIC-YAML") + .required(true) + .build()); + options.addOption( + Option.builder() + .longOpt("package_yaml") + .desc("The package metadata YAML configuration file.") + .hasArg() + .argName("PACKAGE-YAML") + .build()); + options.addOption( + Option.builder("o") + .longOpt("output") + .desc("The directory in which to output the generated client library.") + .hasArg() + .argName("OUTPUT-DIRECTORY") + .build()); + options.addOption( + Option.builder() + .longOpt("enabled_artifacts") + .desc( + "Optional. Artifacts enabled for the generator. " + + "Currently supports 'surface' and 'test'.") + .hasArg() + .argName("ENABLED_ARTIFACTS") + .required(false) + .build()); + + CommandLine cl = (new DefaultParser()).parse(options, args); + if (cl.hasOption("help")) { + HelpFormatter formater = new HelpFormatter(); + formater.printHelp("CodeGeneratorTool", options); + } + + try { + generate( + cl.getOptionValue("discovery_doc"), + // cl.getOptionValues("service_yaml"), + cl.getOptionValues("gapic_yaml"), + cl.getOptionValue("package_yaml"), + cl.getOptionValue("output", ""), + cl.getOptionValues("enabled_artifacts")); + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(1); + } + } + + private static void generate( + String discoveryDoc, + // String[] configs, + String[] generatorConfigs, + String packageConfig, + String outputDirectory, + String[] enabledArtifacts) + throws Exception { + ToolOptions options = ToolOptions.create(); + // options.set(ToolOptions.CONFIG_FILES, Lists.newArrayList(configs)); + options.set(DiscoGapicGeneratorApi.DISCOVERY_DOC, discoveryDoc); + options.set(CodeGeneratorApi.OUTPUT_FILE, outputDirectory); + options.set(CodeGeneratorApi.GENERATOR_CONFIG_FILES, Lists.newArrayList(generatorConfigs)); + options.set(CodeGeneratorApi.PACKAGE_CONFIG_FILE, packageConfig); + + if (enabledArtifacts != null) { + options.set(CodeGeneratorApi.ENABLED_ARTIFACTS, Lists.newArrayList(enabledArtifacts)); + } + DiscoGapicGeneratorApi codeGen = new DiscoGapicGeneratorApi(options); + codeGen.run(); + } +} diff --git a/src/main/java/com/google/api/codegen/config/GapicProductConfig.java b/src/main/java/com/google/api/codegen/config/GapicProductConfig.java index bc27c493db..0dc86cd856 100644 --- a/src/main/java/com/google/api/codegen/config/GapicProductConfig.java +++ b/src/main/java/com/google/api/codegen/config/GapicProductConfig.java @@ -22,6 +22,8 @@ import com.google.api.codegen.LanguageSettingsProto; import com.google.api.codegen.LicenseHeaderProto; import com.google.api.codegen.ResourceNameTreatment; +import com.google.api.codegen.discogapic.DiscoGapicInterfaceConfig; +import com.google.api.codegen.discovery.Document; import com.google.api.tools.framework.model.Diag; import com.google.api.tools.framework.model.DiagCollector; import com.google.api.tools.framework.model.Field; @@ -50,7 +52,7 @@ */ @AutoValue public abstract class GapicProductConfig implements ProductConfig { - abstract ImmutableMap getInterfaceConfigMap(); + abstract ImmutableMap getInterfaceConfigMap(); /** Returns the package name. */ public abstract String getPackageName(); @@ -107,7 +109,7 @@ public static GapicProductConfig create(Model model, ConfigProto configProto) { settings = LanguageSettingsProto.getDefaultInstance(); } - ImmutableMap interfaceConfigMap = + ImmutableMap interfaceConfigMap = createInterfaceConfigMap( model.getDiagCollector(), configProto, @@ -150,16 +152,26 @@ public static GapicProductConfig create(Model model, ConfigProto configProto) { } } + @Nullable + public static GapicProductConfig create(Document document, ConfigProto configProto) { + ImmutableMap interfaceConfigMap = + ImmutableMap.builder() + .put(document.name(), DiscoGapicInterfaceConfig.createInterfaceConfig(document)) + .build(); + // TODO actually load data + return createDummyInstance(interfaceConfigMap, "", "", null); + } + /** Creates an GapicProductConfig with no content. Exposed for testing. */ @VisibleForTesting public static GapicProductConfig createDummyInstance() { - return createDummyInstance(ImmutableMap.of(), "", "", null); + return createDummyInstance(ImmutableMap.of(), "", "", null); } /** Creates an GapicProductConfig with fixed content. Exposed for testing. */ @VisibleForTesting public static GapicProductConfig createDummyInstance( - ImmutableMap interfaceConfigMap, + ImmutableMap interfaceConfigMap, String packageName, String domainLayerLocation, ResourceNameMessageConfigs messageConfigs) { @@ -175,15 +187,15 @@ public static GapicProductConfig createDummyInstance( messageConfigs, ImmutableMap.of())); } - private static ImmutableMap createInterfaceConfigMap( + private static ImmutableMap createInterfaceConfigMap( DiagCollector diagCollector, ConfigProto configProto, LanguageSettingsProto languageSettings, ResourceNameMessageConfigs messageConfigs, ImmutableMap resourceNameConfigs, SymbolTable symbolTable) { - ImmutableMap.Builder interfaceConfigMap = - ImmutableMap.builder(); + ImmutableMap.Builder interfaceConfigMap = + ImmutableMap.builder(); for (InterfaceConfigProto interfaceConfigProto : configProto.getInterfacesList()) { Interface apiInterface = symbolTable.lookupInterface(interfaceConfigProto.getName()); if (apiInterface == null || !apiInterface.isReachable()) { @@ -385,7 +397,11 @@ private static ImmutableMap createResponseFieldConfigMap( /** Returns the GapicInterfaceConfig for the given API interface. */ public GapicInterfaceConfig getInterfaceConfig(Interface apiInterface) { - return getInterfaceConfigMap().get(apiInterface.getFullName()); + return (GapicInterfaceConfig) getInterfaceConfigMap().get(apiInterface.getFullName()); + } + + public InterfaceConfig getInterfaceConfig(String fullName) { + return getInterfaceConfigMap().get(fullName); } public Iterable getSingleResourceNameConfigs() { diff --git a/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceConfig.java b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceConfig.java new file mode 100644 index 0000000000..946967df1e --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceConfig.java @@ -0,0 +1,30 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic; + +import com.google.api.codegen.config.InterfaceConfig; +import com.google.api.codegen.discovery.Document; +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class DiscoGapicInterfaceConfig implements InterfaceConfig { + + @Override + public abstract String getName(); + + public static DiscoGapicInterfaceConfig createInterfaceConfig(Document document) { + return new AutoValue_DiscoGapicInterfaceConfig(document.name()); + } +} diff --git a/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceContext.java b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceContext.java new file mode 100644 index 0000000000..a5b994962d --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicInterfaceContext.java @@ -0,0 +1,73 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic; + +import com.google.api.codegen.config.GapicMethodConfig; +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.transformer.FeatureConfig; +import com.google.api.codegen.transformer.InterfaceContext; +import com.google.api.codegen.transformer.ModelTypeTable; +import com.google.api.codegen.transformer.SurfaceNamer; +import com.google.api.codegen.util.TypeTable; +import com.google.api.tools.framework.model.Method; +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class DiscoGapicInterfaceContext implements InterfaceContext { + public static DiscoGapicInterfaceContext create( + Document document, + GapicProductConfig productConfig, + ModelTypeTable typeTable, + SurfaceNamer namer, + FeatureConfig featureConfig) { + return new AutoValue_DiscoGapicInterfaceContext( + document, productConfig, typeTable, namer, featureConfig); + } + + public abstract Document getDocument(); + + @Override + public abstract GapicProductConfig getProductConfig(); + + public abstract ModelTypeTable getModelTypeTable(); + + @Override + public TypeTable getTypeTable() { + return getModelTypeTable().getTypeTable(); + } + + @Override + public abstract SurfaceNamer getNamer(); + + public abstract FeatureConfig getFeatureConfig(); + + public DiscoGapicInterfaceContext withNewTypeTable() { + return create( + getDocument(), + getProductConfig(), + getModelTypeTable().cloneEmpty(), + getNamer(), + getFeatureConfig()); + } + + public DiscoGapicInterfaceConfig getInterfaceConfig() { + return (DiscoGapicInterfaceConfig) getProductConfig().getInterfaceConfig(getDocument().name()); + } + + public GapicMethodConfig getMethodConfig(Method method) { + return null; + } +} diff --git a/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProvider.java b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProvider.java new file mode 100644 index 0000000000..accc31a120 --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProvider.java @@ -0,0 +1,107 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic; + +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.discogapic.transformer.DocumentToViewTransformer; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.rendering.CommonSnippetSetRunner; +import com.google.api.codegen.viewmodel.ViewModel; +import com.google.api.tools.framework.snippet.Doc; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class DiscoGapicProvider { + private final Document document; + private final GapicProductConfig productConfig; + private final CommonSnippetSetRunner snippetSetRunner; + private final DocumentToViewTransformer transformer; + + private DiscoGapicProvider( + Document document, + GapicProductConfig productConfig, + CommonSnippetSetRunner snippetSetRunner, + DocumentToViewTransformer transformer) { + this.document = document; + this.productConfig = productConfig; + this.snippetSetRunner = snippetSetRunner; + this.transformer = transformer; + } + + public List getSnippetFileNames() { + return transformer.getTemplateFileNames(); + } + + public Map generate() { + return generate(null); + } + + public Map generate(String snippetFileName) { + List surfaceDocs = transformer.transform(document, productConfig); + + Map docs = new TreeMap<>(); + for (ViewModel surfaceDoc : surfaceDocs) { + if (snippetFileName != null && !surfaceDoc.templateFileName().equals(snippetFileName)) { + continue; + } + Doc doc = snippetSetRunner.generate(surfaceDoc); + if (doc == null) { + // generation failed; failures are captured in the model. + continue; + } + docs.put(surfaceDoc.outputPath(), doc); + } + + return docs; + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder { + private Document document; + private GapicProductConfig productConfig; + private CommonSnippetSetRunner snippetSetRunner; + private DocumentToViewTransformer transformer; + + private Builder() {} + + public Builder setDocument(Document document) { + this.document = document; + return this; + } + + public Builder setProductConfig(GapicProductConfig productConfig) { + this.productConfig = productConfig; + return this; + } + + public Builder setSnippetSetRunner(CommonSnippetSetRunner snippetSetRunner) { + this.snippetSetRunner = snippetSetRunner; + return this; + } + + public Builder setDocumentToViewTransformer(DocumentToViewTransformer transformer) { + this.transformer = transformer; + return this; + } + + public DiscoGapicProvider build() { + return new DiscoGapicProvider(document, productConfig, snippetSetRunner, transformer); + } + } +} diff --git a/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProviderFactory.java b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProviderFactory.java new file mode 100644 index 0000000000..88418debac --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/DiscoGapicProviderFactory.java @@ -0,0 +1,29 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic; + +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.config.PackageMetadataConfig; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.gapic.GapicGeneratorConfig; +import java.util.List; + +public interface DiscoGapicProviderFactory { + List create( + Document document, + GapicProductConfig productConfig, + GapicGeneratorConfig generatorConfig, + PackageMetadataConfig packageConfig); +} diff --git a/src/main/java/com/google/api/codegen/discogapic/MainDiscoGapicProviderFactory.java b/src/main/java/com/google/api/codegen/discogapic/MainDiscoGapicProviderFactory.java new file mode 100644 index 0000000000..2bff0c134c --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/MainDiscoGapicProviderFactory.java @@ -0,0 +1,79 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic; + +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.config.PackageMetadataConfig; +import com.google.api.codegen.discogapic.transformer.java.JavaDiscoGapicSurfaceTransformer; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.gapic.CommonGapicCodePathMapper; +import com.google.api.codegen.gapic.GapicCodePathMapper; +import com.google.api.codegen.gapic.GapicGeneratorConfig; +import com.google.api.codegen.rendering.CommonSnippetSetRunner; +import com.google.api.codegen.util.java.JavaRenderingUtil; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.NotImplementedException; + +public class MainDiscoGapicProviderFactory implements DiscoGapicProviderFactory { + + public static final String JAVA = "java"; + + /** Create the DiscoGapicProvider based on the given id */ + public static List defaultCreate( + Document document, + GapicProductConfig productConfig, + GapicGeneratorConfig generatorConfig, + PackageMetadataConfig packageConfig) { + + ArrayList providers = new ArrayList<>(); + String id = generatorConfig.id(); + + // Please keep the following IDs in alphabetical order + if (id.equals(JAVA)) { + if (generatorConfig.enableSurfaceGenerator()) { + GapicCodePathMapper javaPathMapper = + CommonGapicCodePathMapper.newBuilder() + .setPrefix("src/main/java") + .setShouldAppendPackage(true) + .build(); + DiscoGapicProvider mainProvider = + DiscoGapicProvider.newBuilder() + .setDocument(document) + .setProductConfig(productConfig) + .setSnippetSetRunner(new CommonSnippetSetRunner(new JavaRenderingUtil())) + .setDocumentToViewTransformer( + new JavaDiscoGapicSurfaceTransformer(javaPathMapper, packageConfig)) + .build(); + + providers.add(mainProvider); + } + return providers; + + } else { + throw new NotImplementedException("DiscoGapicProviderFactory: invalid id \"" + id + "\""); + } + } + + /** Create the DiscoGapicProvider based on the given id */ + @Override + public List create( + Document document, + GapicProductConfig productConfig, + GapicGeneratorConfig generatorConfig, + PackageMetadataConfig packageConfig) { + return defaultCreate(document, productConfig, generatorConfig, packageConfig); + } +} diff --git a/src/main/java/com/google/api/codegen/discogapic/transformer/DocumentToViewTransformer.java b/src/main/java/com/google/api/codegen/discogapic/transformer/DocumentToViewTransformer.java new file mode 100644 index 0000000000..cc04a1fa39 --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/transformer/DocumentToViewTransformer.java @@ -0,0 +1,26 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic.transformer; + +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.viewmodel.ViewModel; +import java.util.List; + +public interface DocumentToViewTransformer { + List transform(Document document, GapicProductConfig productConfig); + + List getTemplateFileNames(); +} diff --git a/src/main/java/com/google/api/codegen/discogapic/transformer/java/JavaDiscoGapicSurfaceTransformer.java b/src/main/java/com/google/api/codegen/discogapic/transformer/java/JavaDiscoGapicSurfaceTransformer.java new file mode 100644 index 0000000000..511a951044 --- /dev/null +++ b/src/main/java/com/google/api/codegen/discogapic/transformer/java/JavaDiscoGapicSurfaceTransformer.java @@ -0,0 +1,157 @@ +/* Copyright 2017 Google Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.api.codegen.discogapic.transformer.java; + +import com.google.api.codegen.ReleaseLevel; +import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.config.PackageMetadataConfig; +import com.google.api.codegen.discogapic.DiscoGapicInterfaceContext; +import com.google.api.codegen.discogapic.transformer.DocumentToViewTransformer; +import com.google.api.codegen.discovery.Document; +import com.google.api.codegen.gapic.GapicCodePathMapper; +import com.google.api.codegen.transformer.FileHeaderTransformer; +import com.google.api.codegen.transformer.ModelTypeTable; +import com.google.api.codegen.transformer.StandardImportSectionTransformer; +import com.google.api.codegen.transformer.SurfaceNamer; +import com.google.api.codegen.transformer.java.JavaFeatureConfig; +import com.google.api.codegen.transformer.java.JavaModelTypeNameConverter; +import com.google.api.codegen.transformer.java.JavaSurfaceNamer; +import com.google.api.codegen.util.java.JavaTypeTable; +import com.google.api.codegen.viewmodel.ApiCallableView; +import com.google.api.codegen.viewmodel.FormatResourceFunctionView; +import com.google.api.codegen.viewmodel.ParseResourceFunctionView; +import com.google.api.codegen.viewmodel.PathTemplateView; +import com.google.api.codegen.viewmodel.ServiceDocView; +import com.google.api.codegen.viewmodel.StaticLangApiFileView; +import com.google.api.codegen.viewmodel.StaticLangApiMethodView; +import com.google.api.codegen.viewmodel.StaticLangApiView; +import com.google.api.codegen.viewmodel.ViewModel; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class JavaDiscoGapicSurfaceTransformer implements DocumentToViewTransformer { + private final GapicCodePathMapper pathMapper; + private final StandardImportSectionTransformer importSectionTransformer = + new StandardImportSectionTransformer(); + private final FileHeaderTransformer fileHeaderTransformer = + new FileHeaderTransformer(importSectionTransformer); + + private static final String XAPI_TEMPLATE_FILENAME = "java/main.snip"; + private static final String XSETTINGS_TEMPLATE_FILENAME = "java/settings.snip"; + private static final String PACKAGE_INFO_TEMPLATE_FILENAME = "java/package-info.snip"; + private static final String PAGE_STREAMING_RESPONSE_TEMPLATE_FILENAME = + "java/page_streaming_response.snip"; + + public JavaDiscoGapicSurfaceTransformer( + GapicCodePathMapper pathMapper, PackageMetadataConfig packageMetadataConfig) { + this.pathMapper = pathMapper; + // TODO use packageMetadataConfig + } + + @Override + public List getTemplateFileNames() { + return Arrays.asList( + XAPI_TEMPLATE_FILENAME, + XSETTINGS_TEMPLATE_FILENAME, + PACKAGE_INFO_TEMPLATE_FILENAME, + PAGE_STREAMING_RESPONSE_TEMPLATE_FILENAME); + } + + @Override + public List transform(Document document, GapicProductConfig productConfig) { + List surfaceDocs = new ArrayList<>(); + SurfaceNamer namer = new JavaSurfaceNamer(productConfig.getPackageName()); + + List serviceDocs = new ArrayList<>(); + + DiscoGapicInterfaceContext context = + DiscoGapicInterfaceContext.create( + document, + productConfig, + createTypeTable(productConfig.getPackageName()), + namer, + JavaFeatureConfig.newBuilder().enableStringFormatFunctions(false).build()); + StaticLangApiFileView apiFile = generateApiFile(context); + surfaceDocs.add(apiFile); + + serviceDocs.add(apiFile.api().doc()); + + return surfaceDocs; + } + + private ModelTypeTable createTypeTable(String implicitPackageName) { + return new ModelTypeTable( + new JavaTypeTable(implicitPackageName), + new JavaModelTypeNameConverter(implicitPackageName)); + } + + private StaticLangApiFileView generateApiFile(DiscoGapicInterfaceContext context) { + StaticLangApiFileView.Builder apiFile = StaticLangApiFileView.newBuilder(); + + apiFile.templateFileName(XAPI_TEMPLATE_FILENAME); + + apiFile.api(generateApiClass(context)); + + String outputPath = pathMapper.getOutputPath(null, context.getProductConfig()); + String className = context.getNamer().getApiWrapperClassName(context.getInterfaceConfig()); + apiFile.outputPath(outputPath + File.separator + className + ".java"); + + // must be done as the last step to catch all imports + apiFile.fileHeader(fileHeaderTransformer.generateFileHeader(context)); + + return apiFile.build(); + } + + private StaticLangApiView generateApiClass(DiscoGapicInterfaceContext context) { + addApiImports(context); + + List methods = new ArrayList<>(); + + StaticLangApiView.Builder xapiClass = StaticLangApiView.newBuilder(); + + String name = context.getNamer().getApiWrapperClassName(context.getInterfaceConfig()); + xapiClass.releaseLevelAnnotation(context.getNamer().getReleaseAnnotation(ReleaseLevel.ALPHA)); + xapiClass.name(name); + xapiClass.settingsClassName( + context.getNamer().getApiSettingsClassName(context.getInterfaceConfig())); + xapiClass.apiCallableMembers(new ArrayList()); + xapiClass.pathTemplates(new ArrayList()); + xapiClass.formatResourceFunctions(new ArrayList()); + xapiClass.parseResourceFunctions(new ArrayList()); + xapiClass.apiMethods(methods); + xapiClass.hasDefaultInstance(true); + xapiClass.hasLongRunningOperations(false); + + return xapiClass.build(); + } + + private void addApiImports(DiscoGapicInterfaceContext context) { + ModelTypeTable typeTable = context.getModelTypeTable(); + // TODO several of these can be deleted (e.g. DiscoGapic doesn't use grpc) + typeTable.saveNicknameFor("com.google.api.core.BetaApi"); + typeTable.saveNicknameFor("com.google.api.gax.grpc.ChannelAndExecutor"); + typeTable.saveNicknameFor("com.google.api.gax.grpc.UnaryCallable"); + typeTable.saveNicknameFor("com.google.api.pathtemplate.PathTemplate"); + typeTable.saveNicknameFor("io.grpc.ManagedChannel"); + typeTable.saveNicknameFor("java.io.Closeable"); + typeTable.saveNicknameFor("java.io.IOException"); + typeTable.saveNicknameFor("java.util.ArrayList"); + typeTable.saveNicknameFor("java.util.List"); + typeTable.saveNicknameFor("java.util.concurrent.ScheduledExecutorService"); + typeTable.saveNicknameFor("javax.annotation.Generated"); + } +} diff --git a/src/main/java/com/google/api/codegen/transformer/SurfaceNamer.java b/src/main/java/com/google/api/codegen/transformer/SurfaceNamer.java index f68da452cb..d375e31828 100644 --- a/src/main/java/com/google/api/codegen/transformer/SurfaceNamer.java +++ b/src/main/java/com/google/api/codegen/transformer/SurfaceNamer.java @@ -576,7 +576,7 @@ protected String getInterfaceName(InterfaceConfig interfaceConfig) { /** The name of the class that implements a particular proto interface. */ public String getApiWrapperClassName(InterfaceConfig interfaceConfig) { - return publicClassName(Name.upperCamel(getInterfaceName(interfaceConfig), "Client")); + return publicClassName(Name.anyCamel(getInterfaceName(interfaceConfig), "Client")); } /** The name of the implementation class that implements a particular proto interface. */ @@ -592,8 +592,8 @@ public String getApiSnippetsClassName(Interface apiInterface) { /** * The name of the settings class for a particular proto interface; not used in most languages. */ - public String getApiSettingsClassName(GapicInterfaceConfig interfaceConfig) { - return publicClassName(Name.upperCamel(getInterfaceName(interfaceConfig), "Settings")); + public String getApiSettingsClassName(InterfaceConfig interfaceConfig) { + return publicClassName(Name.anyCamel(getInterfaceName(interfaceConfig), "Settings")); } /** The name of the class that contains paged list response wrappers. */ diff --git a/src/main/java/com/google/api/codegen/util/Name.java b/src/main/java/com/google/api/codegen/util/Name.java index 4108728b14..e7bb137cac 100644 --- a/src/main/java/com/google/api/codegen/util/Name.java +++ b/src/main/java/com/google/api/codegen/util/Name.java @@ -61,16 +61,7 @@ public static Name upperUnderscore(String... pieces) { * @throws IllegalArgumentException if any of the strings do not follow the camel format. */ public static Name anyCamel(String... pieces) { - List namePieces = new ArrayList<>(); - for (String piece : pieces) { - validateCamel(piece, CheckCase.NO_CHECK); - CaseFormat format = CaseFormat.LOWER_CAMEL; - if (Character.isUpperCase(piece.charAt(0))) { - format = CaseFormat.UPPER_CAMEL; - } - namePieces.add(new NamePiece(piece, format)); - } - return new Name(namePieces); + return camelInternal(CheckCase.NO_CHECK, AcronymMode.CAMEL_CASE, pieces); } /** @@ -79,12 +70,7 @@ public static Name anyCamel(String... pieces) { * @throws IllegalArgumentException if any of the strings do not follow the lower-camel format. */ public static Name lowerCamel(String... pieces) { - List namePieces = new ArrayList<>(); - for (String piece : pieces) { - validateCamel(piece, CheckCase.LOWER); - namePieces.add(new NamePiece(piece, CaseFormat.LOWER_CAMEL)); - } - return new Name(namePieces); + return camelInternal(CheckCase.LOWER, AcronymMode.CAMEL_CASE, pieces); } /** @@ -93,19 +79,28 @@ public static Name lowerCamel(String... pieces) { * @throws IllegalArgumentException if any of the strings do not follow the upper-camel format. */ public static Name upperCamel(String... pieces) { - return upperCamelInternal(AcronymMode.CAMEL_CASE, pieces); + return camelInternal(CheckCase.UPPER, AcronymMode.CAMEL_CASE, pieces); } public static Name upperCamelKeepUpperAcronyms(String... pieces) { - return upperCamelInternal(AcronymMode.UPPER_CASE, pieces); + return camelInternal(CheckCase.UPPER, AcronymMode.UPPER_CASE, pieces); + } + + private static CaseFormat getCamelCaseFormat(String piece) { + if (Character.isUpperCase(piece.charAt(0))) { + return CaseFormat.UPPER_CAMEL; + } else { + return CaseFormat.LOWER_CAMEL; + } } - private static Name upperCamelInternal(AcronymMode acronymMode, String... pieces) { + private static Name camelInternal( + CheckCase checkCase, AcronymMode acronymMode, String... pieces) { List namePieces = new ArrayList<>(); for (String piece : pieces) { - validateCamel(piece, CheckCase.UPPER); + validateCamel(piece, checkCase); for (SubNamePiece subPiece : CommonAcronyms.splitByUpperAcronyms(piece)) { - CaseFormat caseFormat = CaseFormat.UPPER_CAMEL; + CaseFormat caseFormat = getCamelCaseFormat(subPiece.namePieceString()); CasingMode casingMode = CasingMode.NORMAL; if (subPiece.type().equals(NamePieceCasingType.UPPER_ACRONYM)) { caseFormat = CaseFormat.UPPER_UNDERSCORE; diff --git a/src/main/java/com/google/api/codegen/viewmodel/StaticLangApiView.java b/src/main/java/com/google/api/codegen/viewmodel/StaticLangApiView.java index 8dff2ca46b..b0100131ba 100644 --- a/src/main/java/com/google/api/codegen/viewmodel/StaticLangApiView.java +++ b/src/main/java/com/google/api/codegen/viewmodel/StaticLangApiView.java @@ -20,8 +20,13 @@ @AutoValue public abstract class StaticLangApiView { + @Nullable public abstract ServiceDocView doc(); + public boolean hasDoc() { + return doc() != null; + } + @Nullable public abstract String releaseLevelAnnotation(); diff --git a/src/main/resources/com/google/api/codegen/java/java_discogapic.yaml b/src/main/resources/com/google/api/codegen/java/java_discogapic.yaml new file mode 100644 index 0000000000..ea454e5200 --- /dev/null +++ b/src/main/resources/com/google/api/codegen/java/java_discogapic.yaml @@ -0,0 +1,5 @@ +type: com.google.api.codegen.ConfigProto +language: java +generator: + factory: com.google.api.codegen.discogapic.MainDiscoGapicProviderFactory + id: java diff --git a/src/main/resources/com/google/api/codegen/java/main.snip b/src/main/resources/com/google/api/codegen/java/main.snip index d07d0b609c..475baefd4f 100644 --- a/src/main/resources/com/google/api/codegen/java/main.snip +++ b/src/main/resources/com/google/api/codegen/java/main.snip @@ -4,7 +4,9 @@ @snippet generate(apiFile) {@renderFileHeader(apiFile.fileHeader)} - {@serviceDoc(apiFile.api)} + @if apiFile.api.hasDoc + {@serviceDoc(apiFile.api)} + @end @@Generated("by GAPIC") @if apiFile.api.releaseLevelAnnotation {@apiFile.api.releaseLevelAnnotation} diff --git a/src/test/java/com/google/api/codegen/PhpGapicCodePathMapperTest.java b/src/test/java/com/google/api/codegen/PhpGapicCodePathMapperTest.java index 514d48db95..8583c686d8 100644 --- a/src/test/java/com/google/api/codegen/PhpGapicCodePathMapperTest.java +++ b/src/test/java/com/google/api/codegen/PhpGapicCodePathMapperTest.java @@ -14,8 +14,8 @@ */ package com.google.api.codegen; -import com.google.api.codegen.config.GapicInterfaceConfig; import com.google.api.codegen.config.GapicProductConfig; +import com.google.api.codegen.config.InterfaceConfig; import com.google.api.codegen.php.PhpGapicCodePathMapper; import com.google.api.gax.grpc.ApiCallable; import com.google.common.collect.ImmutableMap; @@ -39,7 +39,7 @@ public void getOutputPathTest() { GapicProductConfig configWithGoogleCloud = GapicProductConfig.createDummyInstance( - ImmutableMap.of(), + ImmutableMap.of(), "Google\\Cloud\\Sample\\Package\\V1", "", null); @@ -48,7 +48,7 @@ public void getOutputPathTest() { GapicProductConfig configWithGoogleNonCloud = GapicProductConfig.createDummyInstance( - ImmutableMap.of(), + ImmutableMap.of(), "Google\\NonCloud\\Sample\\Package\\V1", "", null); @@ -57,7 +57,7 @@ public void getOutputPathTest() { GapicProductConfig configWithAlphabet = GapicProductConfig.createDummyInstance( - ImmutableMap.of(), + ImmutableMap.of(), "Alphabet\\Google\\Cloud\\Sample\\Package\\V1", "", null);