Skip to content

Commit

Permalink
Add DataPath util (#2212)
Browse files Browse the repository at this point in the history
  • Loading branch information
MinnDevelopment committed Sep 11, 2022
1 parent 8d72ce7 commit 9b32e6b
Show file tree
Hide file tree
Showing 11 changed files with 1,399 additions and 49 deletions.
38 changes: 38 additions & 0 deletions src/main/java/net/dv8tion/jda/api/utils/data/DataArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,44 @@ public long getUnsignedLong(int index, long defaultValue)
return value == null ? defaultValue : value;
}

/**
* Resolves the value at the specified index to a double.
*
* @param index
* The index to resolve
*
* @throws net.dv8tion.jda.api.exceptions.ParsingException
* If the value is of the wrong type
*
* @return The resolved double value
*/
public double getDouble(int index)
{
Double value = get(Double.class, index, Double::parseDouble, Number::doubleValue);
if (value == null)
throw valueError(index, "double");
return value;
}

/**
* Resolves the value at the specified index to a double.
*
* @param index
* The index to resolve
* @param defaultValue
* Alternative value to use when the value associated with the index is null
*
* @throws net.dv8tion.jda.api.exceptions.ParsingException
* If the value is of the wrong type
*
* @return The resolved double value
*/
public double getDouble(int index, double defaultValue)
{
Double value = get(Double.class, index, Double::parseDouble, Number::doubleValue);
return value == null ? defaultValue : value;
}

/**
* Appends the provided value to the end of the array.
*
Expand Down
1,206 changes: 1,206 additions & 0 deletions src/main/java/net/dv8tion/jda/api/utils/data/DataPath.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ public InteractionCreateHandler(JDAImpl api)
protected Long handleInternally(DataObject content)
{
int type = content.getInt("type");
if (content.getInt("version", 1) != 1)
int version = content.getInt("version", 1);
if (version != 1)
{
WebSocketClient.LOG.debug("Received interaction with version {}. This version is currently unsupported by this version of JDA. Consider updating!", content.getInt("version", 1));
WebSocketClient.LOG.debug("Received interaction with version {}. This version is currently unsupported by this version of JDA. Consider updating!", version);
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,15 @@ protected Long handleInternally(DataObject content)
}

DataObject selfJson = content.getObject("user");
// Inject the application id which isn't added to the self user by default
selfJson.put("application_id", // Used to update SelfUser#getApplicationId
content.optObject("application")
.map(obj -> obj.getUnsignedLong("id"))
.orElse(selfJson.getUnsignedLong("id"))
);

// SelfUser is already created in login(...) but this just updates it to the current state from the api, and injects the application id
builder.createSelfUser(selfJson);

if (getJDA().getGuildSetupController().setIncompleteCount(distinctGuilds.size()))
{
distinctGuilds.forEachEntry((id, guild) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.localization.LocalizationMap;
import net.dv8tion.jda.api.interactions.commands.privileges.IntegrationPrivilege;
Expand All @@ -35,7 +35,9 @@
import net.dv8tion.jda.internal.utils.localization.LocalizationUtils;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -87,10 +89,10 @@ public CommandImpl(JDAImpl api, Guild guild, DataObject json)
public static <T> List<T> parseOptions(DataObject json, Predicate<DataObject> test, Function<DataObject, T> transform)
{
return json.optArray("options").map(arr ->
arr.stream(DataArray::getObject)
.filter(test)
.map(transform)
.collect(Collectors.toList())
arr.stream(DataArray::getObject)
.filter(test)
.map(transform)
.collect(Collectors.toList())
).orElse(Collections.emptyList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ public ReplyCallbackAction deferReply()
public ModalCallbackAction replyModal(@Nonnull Modal modal)
{
Checks.notNull(modal, "Modal");

return new ModalCallbackActionImpl(this, modal);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,32 +109,35 @@ private void parseResolved(JDAImpl jda, DataObject resolveJson)
})
);

if (guild != null) // Technically these can function in DMs too ...
if (this.guild != null)
{
GuildImpl guild = (GuildImpl) this.guild;
resolveJson.optObject("members").ifPresent(members ->
members.keys().forEach(memberId -> {
DataObject userJson = resolveJson.getObject("users").getObject(memberId);
{
DataObject users = resolveJson.getObject("users");
members.keys().forEach(memberId ->
{
DataObject memberJson = members.getObject(memberId);
memberJson.put("user", userJson);
MemberImpl optionMember = entityBuilder.createMember((GuildImpl) guild, memberJson);
memberJson.put("user", users.getObject(memberId)); // Add user json as well for parsing
MemberImpl optionMember = entityBuilder.createMember(guild, memberJson);
entityBuilder.updateMemberCache(optionMember);
resolved.put(optionMember.getIdLong(), optionMember); // This basically upgrades user to member
})
);
});
});
resolveJson.optObject("roles").ifPresent(roles ->
roles.keys()
.stream()
.map(guild::getRoleById)
.map(this.guild::getRoleById)
.filter(Objects::nonNull)
.forEach(role -> resolved.put(role.getIdLong(), role))
);
resolveJson.optObject("channels").ifPresent(channels -> {
resolveJson.optObject("channels").ifPresent(channels ->
channels.keys().forEach(id -> {
ISnowflake channelObj = jda.getGuildChannelById(id);
if (channelObj != null)
resolved.put(channelObj.getIdLong(), channelObj);
});
});
})
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,21 @@
import net.dv8tion.jda.internal.JDAImpl;

import javax.annotation.Nonnull;
import java.util.function.Function;

public abstract class ContextInteractionImpl<T> extends CommandInteractionImpl implements ContextInteraction<T>, CommandInteractionPayloadMixin
{
private final T target;
private final CommandInteractionPayloadImpl payload;

public ContextInteractionImpl(JDAImpl jda, DataObject data, Function<DataObject, T> entityParser)
public ContextInteractionImpl(JDAImpl jda, DataObject data)
{
super(jda, data);
this.payload = new CommandInteractionPayloadImpl(jda, data);
this.target = entityParser.apply(data.getObject("data").getObject("resolved"));
this.target = parse(data, data.getObject("data").getObject("resolved"));
}

protected abstract T parse(DataObject interactionData, DataObject resolved);

@Override
public CommandInteractionPayload getCommandPayload()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,30 @@ public class MessageContextInteractionImpl extends ContextInteractionImpl<Messag
{
public MessageContextInteractionImpl(JDAImpl jda, DataObject data)
{
super(jda, data, resolved -> parse(jda, data, resolved));
super(jda, data);
}

@Override
public MessageChannelUnion getChannel()
{
return (MessageChannelUnion) super.getChannel();
}

private static Message parse(JDAImpl api, DataObject interactionData, DataObject resolved)
protected Message parse(DataObject interaction, DataObject resolved)
{
DataObject messages = resolved.getObject("messages");
DataObject message = messages.getObject(messages.keys().iterator().next());

//Hopefully in the future we can ask 'message.hasKey("guild_id")' instead.
Guild guild = null;
if (!interactionData.isNull("guild_id"))
if (!interaction.isNull("guild_id"))
{
long guildId = interactionData.getUnsignedLong("guild_id");
long guildId = interaction.getUnsignedLong("guild_id");
guild = api.getGuildById(guildId);
if (guild == null)
throw new IllegalStateException("Cannot find guild for resolved message object.");
}

return api.getEntityBuilder().createMessageWithLookup(message, guild, false);
}

@Override
public MessageChannelUnion getChannel()
{
return (MessageChannelUnion) super.getChannel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,25 @@

public class UserContextInteractionImpl extends ContextInteractionImpl<User> implements UserContextInteraction
{
private final MemberImpl member;
private MemberImpl member;

public UserContextInteractionImpl(JDAImpl jda, DataObject data)
{
super(jda, data, (resolved) -> parse(jda, resolved));
DataObject resolved = data.getObject("data").getObject("resolved");
if (!resolved.isNull("members"))
{
DataObject members = resolved.getObject("members");
DataObject member = members.getObject(members.keys().iterator().next());
this.member = jda.getEntityBuilder().createMember((GuildImpl) guild, member);
jda.getEntityBuilder().updateMemberCache(this.member);
}
else
{
this.member = null;
}
super(jda, data);
}

private static User parse(JDAImpl api, DataObject resolved)
@Override
protected User parse(DataObject interaction, DataObject resolved)
{
DataObject users = resolved.getObject("users");
DataObject user = users.getObject(users.keys().iterator().next());

resolved.optObject("members").filter(m -> !m.keys().isEmpty()).ifPresent(members -> {
DataObject member = members.getObject(members.keys().iterator().next());
this.member = api.getEntityBuilder().createMember((GuildImpl) guild, member);
api.getEntityBuilder().updateMemberCache(this.member);
});

return api.getEntityBuilder().createUser(user);
}

Expand Down
101 changes: 101 additions & 0 deletions src/test/java/DataPathTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* 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.
*/

import net.dv8tion.jda.api.exceptions.ParsingException;
import net.dv8tion.jda.api.utils.data.DataArray;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.api.utils.data.DataPath;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class DataPathTest
{
@Test
void testSimple()
{
DataObject object = DataObject.empty()
.put("foo", "10"); // string to also test parsing

Assertions.assertEquals(10, DataPath.getInt(object, "foo"));

DataArray array = DataArray.empty().add("20");
Assertions.assertEquals(20, DataPath.getInt(array, "[0]"));
}

@Test
void testSimpleMissing()
{
DataObject object = DataObject.empty();

Assertions.assertEquals(0L, DataPath.getLong(object, "foo?", 0));
Assertions.assertThrows(ParsingException.class, () -> DataPath.getLong(object, "foo"));

DataArray array = DataArray.empty();

Assertions.assertTrue(DataPath.getBoolean(array, "[0]?", true));
Assertions.assertThrows(ParsingException.class, () -> DataPath.getObject(array, "[0]"));
}

@Test
void testObjectInArray()
{
DataObject object = DataObject.empty().put("foo", 10.0);
DataArray array = DataArray.empty().add(object);

Assertions.assertEquals(10.0, DataPath.getDouble(array, "[0].foo"));
Assertions.assertEquals(20.0, DataPath.getDouble(array, "[1]?.foo", 20.0));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getDouble(array, "[1].foo"));
}

@Test
void testArrayInObject()
{
DataArray array = DataArray.empty().add("hello");
DataObject object = DataObject.empty().put("foo", array);

Assertions.assertEquals("hello", DataPath.getString(object, "foo[0]"));
Assertions.assertEquals("world", DataPath.getString(object, "foo[1]?", "world"));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getString(object, "foo[1]"));
}

@Test
void testArrayInArray()
{
DataArray array = DataArray.empty().add(DataArray.empty().add("10"));

Assertions.assertEquals(10, DataPath.getUnsignedInt(array, "[0][0]"));
Assertions.assertEquals(20, DataPath.getUnsignedInt(array, "[0][1]?", 20));
Assertions.assertEquals(20, DataPath.getUnsignedInt(array, "[1]?[0]", 20));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getUnsignedInt(array, "[0][1]"));
Assertions.assertThrows(IndexOutOfBoundsException.class, () -> DataPath.getUnsignedInt(array, "[1][0]"));
Assertions.assertThrows(ParsingException.class, () -> DataPath.getUnsignedInt(array, "[0][1]?"));
Assertions.assertThrows(ParsingException.class, () -> DataPath.getUnsignedInt(array, "[1]?[0]"));
}

@Test
void testComplex()
{
DataObject object = DataObject.empty()
.put("array", DataArray.empty()
.add(DataObject.empty()
.put("foo", DataObject.empty()
.put("bar", "hello"))));

Assertions.assertEquals("hello", DataPath.getString(object, "array[0].foo.bar"));
Assertions.assertEquals("world", DataPath.getString(object, "array[0].wrong?.bar", "world"));
Assertions.assertThrows(ParsingException.class, () -> DataPath.getString(object, "array[0].wrong?.bar"));
}
}

0 comments on commit 9b32e6b

Please sign in to comment.