mirror of
https://github.com/google/nomulus
synced 2026-09-12 11:06:29 +00:00
Change premium list command to be based off of mutating command (#1123)
* Change premium list command to be based off of mutating command * Modify test cases and add comments for better readability * Fix typo
This commit is contained in:
@@ -14,40 +14,29 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||
import static google.registry.tools.server.CreateOrUpdatePremiumListAction.INPUT_PARAM;
|
||||
import static google.registry.tools.server.CreateOrUpdatePremiumListAction.NAME_PARAM;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Verify;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.tools.params.PathParameter;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
/**
|
||||
* Base class for specification of command line parameters common to creating and updating premium
|
||||
* lists.
|
||||
*/
|
||||
abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
||||
implements CommandWithConnection, CommandWithRemoteApi {
|
||||
abstract class CreateOrUpdatePremiumListCommand extends MutatingCommand {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
protected List<String> inputData;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = {"-n", "--name"},
|
||||
description = "The name of this premium list (defaults to filename if not specified). "
|
||||
+ "This is almost always the name of the TLD this premium list will be used on.")
|
||||
description =
|
||||
"The name of this premium list (defaults to filename if not specified). "
|
||||
+ "This is almost always the name of the TLD this premium list will be used on.")
|
||||
String name;
|
||||
|
||||
@Parameter(
|
||||
@@ -57,78 +46,17 @@ abstract class CreateOrUpdatePremiumListCommand extends ConfirmingCommand
|
||||
required = true)
|
||||
Path inputFile;
|
||||
|
||||
protected AppEngineConnection connection;
|
||||
protected int inputLineCount;
|
||||
|
||||
@Override
|
||||
public void setConnection(AppEngineConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
abstract String getCommandPath();
|
||||
|
||||
ImmutableMap<String, String> getParameterMap() {
|
||||
return ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() throws Exception {
|
||||
name = isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
List<String> lines = Files.readAllLines(inputFile, UTF_8);
|
||||
// Try constructing and parsing the premium list locally to check up front for validation errors
|
||||
new PremiumList.Builder().setName(name).build().parse(lines);
|
||||
inputLineCount = lines.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String prompt() {
|
||||
return String.format(
|
||||
"You are about to save the premium list %s with %d items: ", name, inputLineCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute() throws Exception {
|
||||
ImmutableMap.Builder<String, String> params = new ImmutableMap.Builder<>();
|
||||
params.put(NAME_PARAM, name);
|
||||
String inputFileContents = new String(Files.readAllBytes(inputFile), UTF_8);
|
||||
String requestBody =
|
||||
Joiner.on('&').withKeyValueSeparator("=").join(
|
||||
ImmutableMap.of(INPUT_PARAM, URLEncoder.encode(inputFileContents, UTF_8.toString())));
|
||||
|
||||
ImmutableMap<String, String> extraParams = getParameterMap();
|
||||
if (extraParams != null) {
|
||||
params.putAll(extraParams);
|
||||
String message = String.format("Saved premium list %s with %d entries", name, inputData.size());
|
||||
try {
|
||||
logger.atInfo().log("Saving premium list for TLD %s", name);
|
||||
PremiumListSqlDao.save(name, inputData);
|
||||
logger.atInfo().log(message);
|
||||
} catch (Throwable e) {
|
||||
message = "Unexpected error saving premium list from nomulus tool command";
|
||||
logger.atSevere().withCause(e).log(message);
|
||||
}
|
||||
|
||||
// Call the server and get the response data
|
||||
String response =
|
||||
connection.sendPostRequest(
|
||||
getCommandPath(), params.build(), MediaType.FORM_DATA, requestBody.getBytes(UTF_8));
|
||||
|
||||
return extractServerResponse(response);
|
||||
}
|
||||
|
||||
// TODO(user): refactor this behavior into a better general-purpose
|
||||
// response validation that can be re-used across the new client/server commands.
|
||||
private String extractServerResponse(String response) {
|
||||
Map<String, Object> responseMap = toMap(JSONValue.parse(stripJsonPrefix(response)));
|
||||
|
||||
// TODO(user): consider using jart's FormField Framework.
|
||||
// See: j/c/g/d/r/ui/server/RegistrarFormFields.java
|
||||
String status = (String) responseMap.get("status");
|
||||
Verify.verify(!status.equals("error"), "Server error: %s", responseMap.get("error"));
|
||||
return String.format("Successfully saved premium list %s\n", name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> toMap(Object obj) {
|
||||
Verify.verify(obj instanceof Map<?, ?>, "JSON object is not a Map: %s", obj);
|
||||
return (Map<String, Object>) obj;
|
||||
}
|
||||
|
||||
// TODO(user): figure out better place to put this method to make it re-usable
|
||||
private static String stripJsonPrefix(String json) {
|
||||
Verify.verify(json.startsWith(JSON_SAFETY_PREFIX));
|
||||
return json.substring(JSON_SAFETY_PREFIX.length());
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,23 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.registry.Registries.assertTldExists;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.base.Strings;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.tools.server.CreatePremiumListAction;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/** Command to create a {@link PremiumList} on Datastore. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Datastore.")
|
||||
/** Command to create a {@link PremiumList} on Database. */
|
||||
@Parameters(separators = " =", commandDescription = "Create a PremiumList in Database.")
|
||||
public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
@Parameter(
|
||||
@@ -29,18 +38,24 @@ public class CreatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
description = "Override restrictions on premium list naming")
|
||||
boolean override;
|
||||
|
||||
/** Returns the path to the servlet task. */
|
||||
@Override
|
||||
public String getCommandPath() {
|
||||
return CreatePremiumListAction.PATH;
|
||||
}
|
||||
|
||||
@Override
|
||||
ImmutableMap<String, String> getParameterMap() {
|
||||
if (override) {
|
||||
return ImmutableMap.of("override", "true");
|
||||
} else {
|
||||
return ImmutableMap.of();
|
||||
// Using CreatePremiumListAction.java as reference;
|
||||
protected void init() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
checkArgument(
|
||||
!PremiumListSqlDao.getLatestRevision(name).isPresent(),
|
||||
"A premium list already exists by this name");
|
||||
if (!override) {
|
||||
// refer to CreatePremiumListAction.java
|
||||
assertTldExists(
|
||||
name,
|
||||
"Premium names must match the name of the TLD they are intended to be used on"
|
||||
+ " (unless --override is specified), yet TLD %s does not exist");
|
||||
}
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
// create a premium list with only input data and store as the first version of the entity
|
||||
PremiumList newPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||
stageEntityChange(
|
||||
null, newPremiumList, VKey.createOfy(PremiumList.class, Key.create(newPremiumList)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,18 +14,86 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.tools.server.UpdatePremiumListAction;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.jpaTm;
|
||||
import static google.registry.util.ListNamingUtils.convertFilePathToName;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/** Command to safely update {@link PremiumList} in Datastore for a given TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Datastore.")
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.googlecode.objectify.Key;
|
||||
import google.registry.model.registry.label.PremiumList;
|
||||
import google.registry.model.registry.label.PremiumList.PremiumListEntry;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.schema.tld.PremiumEntry;
|
||||
import google.registry.schema.tld.PremiumListSqlDao;
|
||||
import google.registry.schema.tld.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.joda.money.BigMoney;
|
||||
|
||||
/** Command to safely update {@link PremiumList} in Database for a given TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.")
|
||||
class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
|
||||
/** Returns the path to the servlet task. */
|
||||
@Override
|
||||
public String getCommandPath() {
|
||||
return UpdatePremiumListAction.PATH;
|
||||
// Using UpdatePremiumListAction.java as reference;
|
||||
protected void init() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
List<String> existingEntry = getExistingPremiumListEntry(name).asList();
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
|
||||
// reconstructing existing premium list to bypass Hibernate lazy initialization exception
|
||||
PremiumList existingPremiumList = PremiumListUtils.parseToPremiumList(name, existingEntry);
|
||||
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, inputData);
|
||||
|
||||
// use LabelsToPrices() for comparison between old and new premium lists since they have
|
||||
// different creation date, updated date even if they have same content;
|
||||
if (!existingPremiumList.getLabelsToPrices().equals(updatedPremiumList.getLabelsToPrices())) {
|
||||
stageEntityChange(
|
||||
existingPremiumList,
|
||||
updatedPremiumList,
|
||||
VKey.createOfy(PremiumList.class, Key.create(existingPremiumList)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
To get premium list content as a set of string. This is a workaround to avoid dealing with
|
||||
Hibernate.LazyInitizationException error. It occurs when trying to access data of the
|
||||
latest revision of an existing premium list.
|
||||
"Cannot evaluate google.registry.model.registry.label.PremiumList.toString()'".
|
||||
Ideally, the following should be the way to verify info in latest revision of a premium list:
|
||||
|
||||
PremiumList existingPremiumList =
|
||||
PremiumListSqlDao.getLatestRevision(name)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format(
|
||||
"Could not update premium list %s because it doesn't exist.", name)));
|
||||
assertThat(persistedList.getLabelsToPrices()).containsEntry("foo", new BigDecimal("9000.00"));
|
||||
assertThat(persistedList.size()).isEqualTo(1);
|
||||
*/
|
||||
protected ImmutableSet<String> getExistingPremiumListEntry(String name) {
|
||||
Optional<PremiumList> list = PremiumListSqlDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
list.isPresent(),
|
||||
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||
Iterable<PremiumEntry> sqlListEntries =
|
||||
jpaTm().transact(() -> PremiumListSqlDao.loadPremiumListEntriesUncached(list.get()));
|
||||
return Streams.stream(sqlListEntries)
|
||||
.map(
|
||||
premiumEntry ->
|
||||
new PremiumListEntry.Builder()
|
||||
.setPrice(
|
||||
BigMoney.of(list.get().getCurrency(), premiumEntry.getPrice()).toMoney())
|
||||
.setLabel(premiumEntry.getDomainLabel())
|
||||
.build()
|
||||
.toString())
|
||||
.collect(toImmutableSet());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user