Skip to content

Commit

Permalink
Codegen- option to generate a schema doc
Browse files Browse the repository at this point in the history
  • Loading branch information
Sunjeet committed Apr 9, 2024
1 parent 7ae0d27 commit 29e1356
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ public abstract class AbstractHollowAPIGeneratorBuilder<B extends AbstractHollow
protected boolean parameterizeAllClassNames = false;
protected boolean useErgonomicShortcuts = false;
protected Path destinationPath;

protected CodeGeneratorConfig config = new CodeGeneratorConfig();

protected abstract G instantiateGenerator();
Expand Down Expand Up @@ -141,6 +140,20 @@ public B withDestination(Path destinationPath) {
return getBuilder();
}

/**
* Enable meta info (e.g. schema doc) and specify the path where it is to be generated.
* @param metaInfoPath location for meta info
* @return this builder
*/
public B withMetaInfo(String metaInfoPath) {
return withMetaInfo(Paths.get(metaInfoPath));
}

public B withMetaInfo(Path metaInfoPath) {
config.setMetaInfoPath(metaInfoPath);
return getBuilder();
}

public G build() {
if (apiClassname == null)
throw new IllegalStateException("Please specify an API classname (.withAPIClassname()) before calling .build()");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.netflix.hollow.api.codegen;

import java.nio.file.Path;

public class CodeGeneratorConfig {
private String classPostfix = "";
private String getterPrefix = "";
Expand All @@ -28,6 +30,22 @@ public class CodeGeneratorConfig {
private boolean restrictApiToFieldType = false;
private boolean useVerboseToString = false;

private boolean useMetaInfo = false;
private Path metaInfoPath;

public void setMetaInfoPath(Path metaInfoPath) {
this.useMetaInfo = true;
this.metaInfoPath = metaInfoPath;
}

public boolean isUseMetaInfo() {
return useMetaInfo;
}

public Path getMetaInfoPath() {
return metaInfoPath;
}

public CodeGeneratorConfig() {}

public CodeGeneratorConfig(String classPostfix, String getterPrefix) {
Expand Down Expand Up @@ -134,6 +152,8 @@ public int hashCode() {
result = prime * result + (useHollowPrimitiveTypes ? 1231 : 1237);
result = prime * result + (usePackageGrouping ? 1231 : 1237);
result = prime * result + (useVerboseToString ? 1231 : 1237);
result = prime * result + (useMetaInfo ? 1231 : 1237);
result = prime * result + ((metaInfoPath == null) ? 0 : metaInfoPath.hashCode());
return result;
}

Expand Down Expand Up @@ -170,6 +190,13 @@ public boolean equals(Object obj) {
return false;
if (useVerboseToString != other.useVerboseToString)
return false;
if (useMetaInfo != other.useMetaInfo)
return false;
if (metaInfoPath == null) {
if (other.metaInfoPath != null)
return false;
} else if (!metaInfoPath.equals(other.metaInfoPath))
return false;
return true;
}

Expand All @@ -194,6 +221,10 @@ public String toString() {
builder.append(restrictApiToFieldType);
builder.append(", useVerboseToString=");
builder.append(useVerboseToString);
builder.append(", useMetaInfo=");
builder.append(useMetaInfo);
builder.append(", metaInfoPath=");
builder.append(metaInfoPath);
builder.append("]");
return builder.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.netflix.hollow.api.codegen;

import static com.netflix.hollow.api.codegen.HollowAPIGenerator.SCHEMA_DOC_SUFFIX;
import static com.netflix.hollow.api.codegen.HollowCodeGenerationUtils.hollowFactoryClassname;
import static com.netflix.hollow.api.codegen.HollowCodeGenerationUtils.hollowObjectProviderName;
import static com.netflix.hollow.api.codegen.HollowCodeGenerationUtils.lowercase;
Expand Down Expand Up @@ -113,6 +114,10 @@ public String generate() {
}
builder.append(" {\n\n");

if (config.isUseMetaInfo()) {
builder.append(" public static final String SCHEMA_DOC = \"" + packageName + "." + className + SCHEMA_DOC_SUFFIX + "\";\n\n");
}

builder.append(" private final HollowObjectCreationSampler objectCreationSampler;\n\n");

for (HollowSchema schema : schemaList) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.netflix.hollow.core.schema.HollowObjectSchema;
import com.netflix.hollow.core.schema.HollowSchema;
import com.netflix.hollow.core.schema.HollowSchema.SchemaType;
import com.netflix.hollow.core.schema.HollowSchemaSorter;
import com.netflix.hollow.core.schema.HollowSetSchema;
import com.netflix.hollow.core.util.HollowWriteStateCreator;
import com.netflix.hollow.core.write.HollowWriteStateEngine;
Expand All @@ -50,6 +51,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
Expand Down Expand Up @@ -118,6 +120,8 @@ public enum GeneratorArguments {

protected CodeGeneratorConfig config = new CodeGeneratorConfig("Hollow", "_"); // NOTE: to be backwards compatible

protected static final String SCHEMA_DOC_SUFFIX = ".schema";

/**
* @param apiClassname the class name of the generated implementation of {@link HollowAPI}
* @param packageName the package name under which all generated classes will be placed
Expand Down Expand Up @@ -434,6 +438,10 @@ public void generateFiles(File directory) throws IOException {
generateFile(directory, hashIndexGenerator);

generateFilesForHollowSchemas(directory);

if (config.isUseMetaInfo()) {
generateMetaInfo(apiClassname);
}
}

/**
Expand Down Expand Up @@ -509,6 +517,30 @@ protected void generateFile(File directory, HollowJavaFileGenerator generator) t
writer.close();
}

private void generateMetaInfo(String apiClassname) throws IOException {
if (config.getMetaInfoPath() == null) {
throw new IllegalStateException("Meta info generation is enabled but path for generating meta info not set");
}

File metaInfoDir = config.getMetaInfoPath().toFile();
if (!metaInfoDir.exists()) metaInfoDir.mkdirs();

// top-level types first
List<HollowSchema> schemas = HollowSchemaSorter.dependencyOrderedSchemaList(dataset);
Collections.reverse(schemas);

// line-separated {@code HollowSchema}s for readability
StringBuilder builder = new StringBuilder();
for(HollowSchema schema : schemas) {
builder.append(schema.toString()).append("\n");
}
String schemaDoc = builder.toString();

FileWriter writer = new FileWriter(new File(metaInfoDir, packageName + "." + apiClassname + SCHEMA_DOC_SUFFIX));
writer.write(schemaDoc);
writer.close();
}

protected HollowJavaFileGenerator getStaticAPIGenerator(HollowSchema schema) {
if(schema instanceof HollowObjectSchema) {
return new TypeAPIObjectJavaGenerator(apiClassname, packageName, (HollowObjectSchema) schema, dataset, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.UnaryOperator;
import org.junit.After;

public class AbstractHollowAPIGeneratorTest {
private String tmpFolder = System.getProperty("java.io.tmpdir");
protected String tmpFolder = System.getProperty("java.io.tmpdir");
protected String sourceFolder = String.format("%s/src", tmpFolder);
protected String clazzFolder = String.format("%s/classes", tmpFolder);
private Path metaInfoPath = null;

void runGenerator(String apiClassName, String packageName, Class<?> clazz,
UnaryOperator<HollowAPIGenerator.Builder> generatorCustomizer) throws Exception {
Expand All @@ -49,16 +52,15 @@ void runGenerator(String apiClassName, String packageName, Class<?> clazz,
.withDestination(sourceFolder).build();
generator.generateSourceFiles();

if(generator.config.isUseMetaInfo()) {
metaInfoPath = generator.config.getMetaInfoPath();
}
// Compile to validate generated files
HollowCodeGenerationCompileUtil.compileSrcFiles(sourceFolder, clazzFolder);
}

protected void assertNonEmptyFileExists(String relativePath) throws IOException {
if (relativePath.startsWith("/")) {
throw new IllegalArgumentException("Relative paths should not start with /");
}
File f = new File(sourceFolder + "/" + relativePath);
assertTrue("File at " + relativePath + " should exist", f.exists() && f.length() > 0L);
protected void assertNonEmptyFileExists(Path absolutePath) {
assertTrue("File at " + absolutePath + " should exist", absolutePath.toFile().exists() && absolutePath.toFile().length() > 0L);
}

void assertClassHasHollowTypeName(String clazz, String typeName) throws IOException, ClassNotFoundException {
Expand All @@ -81,5 +83,8 @@ void assertFileDoesNotExist(String relativePath) {
public void cleanup() {
HollowCodeGenerationCompileUtil.cleanupFolder(new File(sourceFolder), null);
HollowCodeGenerationCompileUtil.cleanupFolder(new File(clazzFolder), null);
if (metaInfoPath != null) {
HollowCodeGenerationCompileUtil.cleanupFolder(metaInfoPath.toFile(), null);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
package com.netflix.hollow.api.codegen;

import static org.junit.Assert.assertEquals;

import com.netflix.hollow.core.schema.HollowSchema;
import com.netflix.hollow.core.schema.HollowSchemaParser;
import com.netflix.hollow.core.schema.SimpleHollowDataset;
import com.netflix.hollow.core.write.objectmapper.HollowPrimaryKey;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import org.junit.Test;

public class HollowAPIGeneratorTest extends AbstractHollowAPIGeneratorTest {

@Test
public void assertMetaInfoAtCustomLocation() throws Exception {
runGenerator("API", "codegen.api", MyClass.class, b -> b.withMetaInfo("meta-info"));
assertNonEmptyFileExists(Paths.get("meta-info"));
}

@Test
public void testSchemaDocAtCustomLocation() throws Exception {
runGenerator("MyClassTestAPI", "codegen.api", MyClass.class,
builder -> builder.withMetaInfo(tmpFolder + "/" + "resources/META-INF/hollow"));
assertNonEmptyFileExists(Paths.get(tmpFolder, "resources/META-INF/hollow/codegen.api.MyClassTestAPI.schema"));
}

@Test
public void testSchemaDocContents() throws Exception {
runGenerator("MyClassTestAPI", "codegen.api", MyClass.class, b -> b.withMetaInfo(Paths.get(sourceFolder)));
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen.api.MyClassTestAPI.schema"));

List<HollowSchema> expected = SimpleHollowDataset.fromClassDefinitions(MyClass.class).getSchemas();
try (InputStream input = new FileInputStream(sourceFolder + "/" + "codegen.api.MyClassTestAPI.schema")) {
List<HollowSchema> actual = HollowSchemaParser.parseCollectionOfSchemas(new BufferedReader(new InputStreamReader(input)));
assertEquals(expected.size(), actual.size());
assertEquals(new HashSet(expected), new HashSet(actual));
}
}

@Test
public void generatesFileUsingDestinationPath() throws Exception {
runGenerator("API", "com.netflix.hollow.example.api.generated", MyClass.class, b -> b);
Expand All @@ -13,15 +52,15 @@ public void generatesFileUsingDestinationPath() throws Exception {
public void testGenerateWithPostfix() throws Exception {
runGenerator("MyClassTestAPI", "codegen.api", MyClass.class,
builder -> builder.withClassPostfix("Generated"));
assertNonEmptyFileExists("codegen/api/StringGenerated.java");
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen/api/StringGenerated.java"));
assertClassHasHollowTypeName("codegen.api.MyClassGenerated", "MyClass");
}

@Test
public void testGenerateWithPostfixAndPackageGrouping() throws Exception {
runGenerator("MyClassTestAPI", "codegen.api", MyClass.class,
builder -> builder.withClassPostfix("Generated").withPackageGrouping());
assertNonEmptyFileExists("codegen/api/core/StringGenerated.java");
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen/api/core/StringGenerated.java"));
}

@Test
Expand All @@ -43,6 +82,7 @@ public void testGenerateWithPostfixAndAggressiveSubstitutions() throws Exception
}

@SuppressWarnings("unused")
@HollowPrimaryKey(fields = "id")
private static class MyClass {
int id;
String foo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.netflix.hollow.api.codegen.HollowCodeGenerationCompileUtil;
import com.netflix.hollow.core.schema.SimpleHollowDataset;
import java.io.File;
import java.nio.file.Paths;
import org.junit.Test;

public class HollowPerformanceAPIGeneratorTest extends AbstractHollowAPIGeneratorTest {
Expand All @@ -16,9 +17,9 @@ public void testGeneratePerformanceApi() throws Exception {
@Test
public void testGeneratedFilesArePlacedInPackageDirectory() throws Exception {
runGenerator("API", "codegen.api", MyClass.class);
assertNonEmptyFileExists("codegen/api/MyClassPerfAPI.java");
assertNonEmptyFileExists("codegen/api/StringPerfAPI.java");
assertNonEmptyFileExists("codegen/api/API.java");
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen/api/MyClassPerfAPI.java"));
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen/api/StringPerfAPI.java"));
assertNonEmptyFileExists(Paths.get(sourceFolder, "codegen/api/API.java"));
}

private void runGenerator(String apiClassName, String packageName, Class<?> clazz) throws Exception {
Expand Down

0 comments on commit 29e1356

Please sign in to comment.