Skip to content

Commit eed1886

Browse files
authored
Implement rdap_query command (#2886)
* Implement rdap_query command * modifying and correcting issues * modifying and correcting issues * modifying and correcting issues * resolving comments * resolving comments * resolving comments * resolving comments * resolving comments * modifying and correcting issues * resolving comments * resolving comments * resolving comments * modifying and correcting issues * modifying and correcting issues * modifying and correcting issues * resolving comments * modifying and correcting issues * resolving comments * Fixing Deduplication in test * Fixing Deduplication in test * resolving comments
1 parent 7149fd3 commit eed1886

File tree

4 files changed

+202
-0
lines changed

4 files changed

+202
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
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 google.registry.tools;
16+
17+
import static com.google.common.base.Preconditions.checkState;
18+
19+
import com.beust.jcommander.Parameter;
20+
import com.beust.jcommander.Parameters;
21+
import com.google.common.collect.ImmutableMap;
22+
import com.google.gson.Gson;
23+
import com.google.gson.GsonBuilder;
24+
import com.google.gson.JsonElement;
25+
import com.google.gson.JsonParser;
26+
import google.registry.config.RegistryConfig.Config;
27+
import google.registry.request.Action.Service;
28+
import jakarta.inject.Inject;
29+
import java.io.IOException;
30+
import java.util.Optional;
31+
import javax.annotation.Nullable;
32+
33+
/** Command to manually perform an authenticated RDAP query. */
34+
@Parameters(separators = " =", commandDescription = "Manually perform an authenticated RDAP query")
35+
public final class RdapQueryCommand implements CommandWithConnection {
36+
37+
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
38+
39+
enum RdapQueryType {
40+
DOMAIN("/rdap/domain/%s"),
41+
DOMAIN_SEARCH("/rdap/domains", "name"),
42+
NAMESERVER("/rdap/nameserver/%s"),
43+
NAMESERVER_SEARCH("/rdap/nameservers", "name"),
44+
ENTITY("/rdap/entity/%s"),
45+
ENTITY_SEARCH("/rdap/entities", "fn");
46+
47+
private final String pathFormat;
48+
private final Optional<String> searchParamKey;
49+
50+
RdapQueryType(String pathFormat) {
51+
this(pathFormat, null);
52+
}
53+
54+
RdapQueryType(String pathFormat, @Nullable String searchParamKey) {
55+
this.pathFormat = pathFormat;
56+
this.searchParamKey = Optional.ofNullable(searchParamKey);
57+
}
58+
59+
String getQueryPath(String queryTerm) {
60+
return String.format(pathFormat, queryTerm);
61+
}
62+
63+
ImmutableMap<String, String> getQueryParameters(String queryTerm) {
64+
return searchParamKey.map(key -> ImmutableMap.of(key, queryTerm)).orElse(ImmutableMap.of());
65+
}
66+
}
67+
68+
@Parameter(names = "--type", description = "The type of RDAP query to perform.", required = true)
69+
private RdapQueryType type;
70+
71+
@Parameter(
72+
description = "The main query term (e.g., a domain name or search pattern).",
73+
required = true)
74+
private String queryTerm;
75+
76+
@Inject ServiceConnection defaultConnection;
77+
78+
@Inject
79+
@Config("useCanary")
80+
boolean useCanary;
81+
82+
@Override
83+
public void setConnection(ServiceConnection connection) {
84+
this.defaultConnection = connection;
85+
}
86+
87+
@Override
88+
public void run() throws IOException {
89+
checkState(defaultConnection != null, "ServiceConnection was not set by RegistryCli.");
90+
91+
String path = type.getQueryPath(queryTerm);
92+
ImmutableMap<String, String> queryParams = type.getQueryParameters(queryTerm);
93+
94+
ServiceConnection pubapiConnection = defaultConnection.withService(Service.PUBAPI, useCanary);
95+
String rdapResponse = pubapiConnection.sendGetRequest(path, queryParams);
96+
97+
JsonElement rdapJson = JsonParser.parseString(rdapResponse);
98+
System.out.println(GSON.toJson(rdapJson));
99+
}
100+
}

core/src/main/java/google/registry/tools/RegistryTool.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public final class RegistryTool {
9898
.put("login", LoginCommand.class)
9999
.put("logout", LogoutCommand.class)
100100
.put("pending_escrow", PendingEscrowCommand.class)
101+
.put("rdap_query", RdapQueryCommand.class)
101102
.put("recreate_billing_recurrences", RecreateBillingRecurrencesCommand.class)
102103
.put("registrar_poc", RegistrarPocCommand.class)
103104
.put("renew_domain", RenewDomainCommand.class)

core/src/main/java/google/registry/tools/RegistryToolComponent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ interface RegistryToolComponent {
135135

136136
void inject(PendingEscrowCommand command);
137137

138+
void inject(RdapQueryCommand command);
139+
138140
void inject(RenewDomainCommand command);
139141

140142
void inject(SaveSqlCredentialCommand command);
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
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 google.registry.tools;
16+
17+
import static org.junit.jupiter.api.Assertions.assertThrows;
18+
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.ArgumentMatchers.anyString;
20+
import static org.mockito.Mockito.verify;
21+
import static org.mockito.Mockito.when;
22+
23+
import com.beust.jcommander.ParameterException;
24+
import com.google.common.collect.ImmutableMap;
25+
import google.registry.request.Action.Service;
26+
import java.io.IOException;
27+
import java.util.Map;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
import org.junit.jupiter.api.extension.ExtendWith;
31+
import org.mockito.Mock;
32+
import org.mockito.junit.jupiter.MockitoExtension;
33+
import org.mockito.junit.jupiter.MockitoSettings;
34+
import org.mockito.quality.Strictness;
35+
36+
/** Unit tests for {@link RdapQueryCommand}. */
37+
@ExtendWith(MockitoExtension.class)
38+
@MockitoSettings(strictness = Strictness.LENIENT)
39+
class RdapQueryCommandTest extends CommandTestCase<RdapQueryCommand> {
40+
41+
@Mock private ServiceConnection mockDefaultConnection;
42+
@Mock private ServiceConnection mockPubapiConnection;
43+
44+
@BeforeEach
45+
void beforeEach() throws IOException {
46+
command.setConnection(mockDefaultConnection);
47+
when(mockDefaultConnection.withService(Service.PUBAPI, false)).thenReturn(mockPubapiConnection);
48+
when(mockPubapiConnection.sendGetRequest(anyString(), any(Map.class))).thenReturn("");
49+
}
50+
51+
@Test
52+
void testSuccess_domainLookup() throws Exception {
53+
runCommand("--type=DOMAIN", "example.dev");
54+
verify(mockPubapiConnection).sendGetRequest("/rdap/domain/example.dev", ImmutableMap.of());
55+
}
56+
57+
@Test
58+
void testSuccess_domainSearch() throws Exception {
59+
runCommand("--type=DOMAIN_SEARCH", "exam*.dev");
60+
verify(mockPubapiConnection)
61+
.sendGetRequest("/rdap/domains", ImmutableMap.of("name", "exam*.dev"));
62+
}
63+
64+
@Test
65+
void testSuccess_nameserverLookup() throws Exception {
66+
runCommand("--type=NAMESERVER", "ns1.example.com");
67+
verify(mockPubapiConnection)
68+
.sendGetRequest("/rdap/nameserver/ns1.example.com", ImmutableMap.of());
69+
}
70+
71+
@Test
72+
void testSuccess_nameserverSearch() throws Exception {
73+
runCommand("--type=NAMESERVER_SEARCH", "ns*.example.com");
74+
verify(mockPubapiConnection)
75+
.sendGetRequest("/rdap/nameservers", ImmutableMap.of("name", "ns*.example.com"));
76+
}
77+
78+
@Test
79+
void testSuccess_entityLookup() throws Exception {
80+
runCommand("--type=ENTITY", "123-FOO");
81+
verify(mockPubapiConnection).sendGetRequest("/rdap/entity/123-FOO", ImmutableMap.of());
82+
}
83+
84+
@Test
85+
void testSuccess_entitySearch() throws Exception {
86+
runCommand("--type=ENTITY_SEARCH", "John*");
87+
verify(mockPubapiConnection).sendGetRequest("/rdap/entities", ImmutableMap.of("fn", "John*"));
88+
}
89+
90+
@Test
91+
void testFailure_missingType() {
92+
assertThrows(ParameterException.class, () -> runCommand("some-term"));
93+
}
94+
95+
@Test
96+
void testFailure_missingQueryTerm() {
97+
assertThrows(ParameterException.class, () -> runCommand("--type=DOMAIN"));
98+
}
99+
}

0 commit comments

Comments
 (0)