Skip to content

Commit 239d067

Browse files
authored
fix: allow overriding source directory per class in GitHub links (#285)
Currently, the doclet assumes the source directory name matches the artifact ID when generating GitHub links. This leads to broken links for libraries where they differ, such as Firestore (artifact: `google-cloud-firestore`, directory: `google-cloud-firestore-admin` for `FirestoreAdminClient`). This change adds an optional `library_path_overrides` map to `RepoMetadata` and updates `ClassBuilder` to check this map using the simple class name before falling back to the artifact ID. Fixes: b/442875200
1 parent 77d7d5a commit 239d067

4 files changed

Lines changed: 99 additions & 6 deletions

File tree

third_party/docfx-doclet-143274/src/main/java/com/google/docfx/doclet/RepoMetadata.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import java.io.IOException;
99
import java.nio.file.Path;
1010
import java.nio.file.Paths;
11+
import java.util.Collections;
12+
import java.util.Map;
1113
import java.util.Objects;
1214
import java.util.Optional;
1315

@@ -49,6 +51,9 @@ public class RepoMetadata {
4951
@SerializedName("recommended_package")
5052
private String recommendedPackage;
5153

54+
@SerializedName("library_path_overrides")
55+
private Map<String, String> libraryPathOverrides;
56+
5257
private String artifactId;
5358

5459
public String getNamePretty() {
@@ -135,6 +140,14 @@ public void setApiId(String apiId) {
135140
this.apiId = apiId;
136141
}
137142

143+
public Map<String, String> getLibraryPathOverrides() {
144+
return this.libraryPathOverrides != null ? this.libraryPathOverrides : Collections.emptyMap();
145+
}
146+
147+
public void setLibraryPathOverrides(Map<String, String> libraryPathOverrides) {
148+
this.libraryPathOverrides = libraryPathOverrides;
149+
}
150+
138151
// artifactId is parsed from distributionName
139152
public String getArtifactId() {
140153
String substrings[] = this.distributionName.split(":");

third_party/docfx-doclet-143274/src/main/java/com/microsoft/build/ClassBuilder.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,16 @@ private void addClientClassInfo(
248248

249249
private String createClientOverviewTable(TypeElement classElement, RepoMetadata repoMetadata) {
250250
String clientURI = classLookup.extractUid(classElement).replaceAll("\\.", "/");
251+
String className = classElement.getSimpleName().toString();
252+
253+
// Check overrides map first, otherwise default to artifactId
254+
String directory =
255+
repoMetadata
256+
.getLibraryPathOverrides()
257+
.getOrDefault(className, repoMetadata.getArtifactId());
258+
251259
String githubSourceLink =
252-
repoMetadata.getGithubLink()
253-
+ "/"
254-
+ repoMetadata.getArtifactId()
255-
+ "/src/main/java/"
256-
+ clientURI
257-
+ ".java";
260+
repoMetadata.getGithubLink() + "/" + directory + "/src/main/java/" + clientURI + ".java";
258261
StringBuilder tableBuilder = new StringBuilder();
259262
tableBuilder
260263
.append("<table>")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.google.docfx.doclet;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import com.google.gson.Gson;
6+
import org.junit.Test;
7+
8+
public class RepoMetadataTest {
9+
10+
@Test
11+
public void testParseWithLibraryPathOverrides() {
12+
String json =
13+
"{ "
14+
+ "\"distribution_name\": \"com.google.cloud:google-cloud-firestore\", "
15+
+ "\"library_path_overrides\": { "
16+
+ " \"FirestoreAdminClient\": \"google-cloud-firestore-admin\" "
17+
+ "}, "
18+
+ "\"repo\": \"googleapis/java-firestore\" "
19+
+ "}";
20+
21+
RepoMetadata metadata = new Gson().fromJson(json, RepoMetadata.class);
22+
23+
assertEquals("google-cloud-firestore", metadata.getArtifactId());
24+
// Verify the map is populated correctly
25+
assertEquals(
26+
"google-cloud-firestore-admin",
27+
metadata.getLibraryPathOverrides().get("FirestoreAdminClient"));
28+
}
29+
}

third_party/docfx-doclet-143274/src/test/java/com/microsoft/build/ClassBuilderTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
package com.microsoft.build;
1717

1818
import static org.junit.Assert.assertEquals;
19+
import static org.junit.Assert.assertTrue;
1920
import static org.mockito.Mockito.when;
2021

22+
import com.google.docfx.doclet.RepoMetadata;
2123
import com.google.testing.compile.CompilationRule;
2224
import com.microsoft.lookup.ClassItemsLookup;
2325
import com.microsoft.lookup.ClassLookup;
@@ -28,6 +30,9 @@
2830
import com.sun.source.util.DocTrees;
2931
import java.io.File;
3032
import java.util.Collection;
33+
import java.util.HashMap;
34+
import java.util.Map;
35+
import javax.lang.model.element.Name; // Required for mocking getSimpleName()
3136
import javax.lang.model.element.TypeElement;
3237
import javax.lang.model.util.Elements;
3338
import jdk.javadoc.doclet.DocletEnvironment;
@@ -89,4 +94,47 @@ public void addConstructorsInfo() {
8994
Collection<MetadataFileItem> constructorItems = container.getItems();
9095
assertEquals("Container should contain 2 constructor items", constructorItems.size(), 2);
9196
}
97+
98+
@Test
99+
public void createClientOverviewTable_usesLibraryPathOverride() {
100+
// 1. Setup Mock RepoMetadata
101+
RepoMetadata repoMetadata = new RepoMetadata();
102+
repoMetadata.setRepo("googleapis/java-firestore");
103+
repoMetadata.setDistributionName("com.google.cloud:google-cloud-firestore:1.0.0");
104+
105+
Map<String, String> overrides = new HashMap<>();
106+
overrides.put("FirestoreAdminClient", "google-cloud-firestore-admin");
107+
repoMetadata.setLibraryPathOverrides(overrides);
108+
109+
// 2. Mock ClassLookup and Element
110+
ClassLookup classLookup = Mockito.mock(ClassLookup.class);
111+
TypeElement classElement = Mockito.mock(TypeElement.class);
112+
113+
Name simpleName = Mockito.mock(Name.class);
114+
when(simpleName.toString()).thenReturn("FirestoreAdminClient");
115+
when(classElement.getSimpleName()).thenReturn(simpleName);
116+
117+
when(classLookup.extractUid(classElement))
118+
.thenReturn("com.google.cloud.firestore.v1.FirestoreAdminClient");
119+
120+
// 3. Test
121+
ClassBuilder builder = new ClassBuilder(null, classLookup, null, null, null, null);
122+
123+
try {
124+
java.lang.reflect.Method method =
125+
ClassBuilder.class.getDeclaredMethod(
126+
"createClientOverviewTable", TypeElement.class, RepoMetadata.class);
127+
method.setAccessible(true);
128+
String html = (String) method.invoke(builder, classElement, repoMetadata);
129+
130+
// 4. Verify link contains "google-cloud-firestore-admin" and the double slash "//"
131+
assertTrue(
132+
"Link should use the override directory",
133+
html.contains(
134+
"googleapis/java-firestore/tree/main//google-cloud-firestore-admin/src/main/java"));
135+
136+
} catch (Exception e) {
137+
throw new RuntimeException(e);
138+
}
139+
}
92140
}

0 commit comments

Comments
 (0)