Skip to content

Commit 30d224a

Browse files
committed
Added GeneratePlainValue() method
1 parent e4171f9 commit 30d224a

File tree

5 files changed

+123
-18
lines changed

5 files changed

+123
-18
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Bencodex.Types;
2+
using Libplanet.Crypto;
3+
using Libplanet.SDK.Action.Attributes;
4+
5+
namespace Libplanet.SDK.Action.Tests.Sample.Actions
6+
{
7+
public class InvalidAction : ActionBase
8+
{
9+
public override Address StorageAddress =>
10+
new Address("0x1000000000000000000000000000000000000000");
11+
12+
[Executable]
13+
public void Add(IValue args)
14+
{
15+
Integer operand = (Integer)args;
16+
Integer stored = GetState(Signer) is IValue value
17+
? (Integer)value
18+
: new Integer(0);
19+
SetState(Signer, new Integer(stored.Value + operand.Value));
20+
}
21+
22+
public void Subtract(IValue args)
23+
{
24+
Integer operand = (Integer)args;
25+
Integer stored = GetState(Signer) is IValue value
26+
? (Integer)value
27+
: new Integer(0);
28+
SetState(Signer, new Integer(stored.Value - operand.Value));
29+
}
30+
}
31+
}

Libplanet.SDK.Action.Tests/Sample/Actions/NumberAction.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,11 @@ public void Multiply(IValue args)
4343
Call<NumberLogAction>(nameof(NumberLogAction.Multiply), new object?[] { operand });
4444
SetState(Signer, new Integer(stored.Value * operand.Value));
4545
}
46+
47+
// Just some random public method for testing.
48+
public void DoNothing()
49+
{
50+
return;
51+
}
4652
}
4753
}

Libplanet.SDK.Action.Tests/Sample/Actions/TextAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using Libplanet.Crypto;
44
using Libplanet.SDK.Action.Attributes;
55

6-
namespace Libplanet.SDK.Action.Tests
6+
namespace Libplanet.SDK.Action.Tests.Sample.Actions
77
{
88
[ActionType("Text")]
99
public class TextAction : ActionBase

Libplanet.SDK.Action.Tests/Sample/SampleActionsTest.cs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Libplanet.Action.Loader;
66
using Libplanet.Action.State;
77
using Libplanet.Crypto;
8+
using Libplanet.SDK.Action.Attributes;
89
using Libplanet.SDK.Action.Tests.Sample.Actions;
910
using Libplanet.Store;
1011
using Libplanet.Store.Trie;
@@ -109,23 +110,23 @@ public void TextAppend(bool commit)
109110
[Fact]
110111
public void InvalidPlainValueForLoading()
111112
{
112-
IValue plainValue = Dictionary.Empty // Invalid type_id
113+
IValue plainValue = Dictionary.Empty // Invalid type_id
113114
.Add("type_id", "Run")
114115
.Add("call", "Append")
115116
.Add("args", "Hello");
116117
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));
117118

118-
plainValue = Dictionary.Empty // Missing type_id
119+
plainValue = Dictionary.Empty // Missing type_id
119120
.Add("call", "Append")
120121
.Add("args", "Hello");
121122
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));
122123

123-
plainValue = Dictionary.Empty // Missing call
124+
plainValue = Dictionary.Empty // Missing call
124125
.Add("type_id", "Number")
125126
.Add("args", 5);
126127
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));
127128

128-
plainValue = Dictionary.Empty // Missing args
129+
plainValue = Dictionary.Empty // Missing args
129130
.Add("type_id", "Number")
130131
.Add("call", "Add");
131132
Assert.Throws<InvalidActionException>(() => _loader.LoadAction(0, plainValue));
@@ -134,7 +135,7 @@ public void InvalidPlainValueForLoading()
134135
[Fact]
135136
public void InvalidPlainValueForExecution()
136137
{
137-
IValue plainValue = Dictionary.Empty // Invalid call
138+
IValue plainValue = Dictionary.Empty // Invalid call
138139
.Add("type_id", "Number")
139140
.Add("call", "Divide")
140141
.Add("args", 5);
@@ -143,7 +144,7 @@ public void InvalidPlainValueForExecution()
143144
Assert.Throws<InvalidOperationException>(() =>
144145
action.Execute(new MockActionContext(address, address, _world)));
145146

146-
plainValue = Dictionary.Empty // Invalid args
147+
plainValue = Dictionary.Empty // Invalid args
147148
.Add("type_id", "Number")
148149
.Add("call", "Add")
149150
.Add("args", "Hello");
@@ -170,5 +171,38 @@ public void CallableAttributeIsRequired()
170171
action.Execute(new MockActionContext(signer, signer, world)))
171172
.InnerException);
172173
}
174+
175+
[Fact]
176+
public void GeneratePlainValue()
177+
{
178+
IValue expected = Dictionary.Empty
179+
.Add("type_id", "Number")
180+
.Add("call", "Add")
181+
.Add("args", 5);
182+
IValue generated = ActionBase.GeneratePlainValue<NumberAction>(
183+
"Add", new Integer(5));
184+
Assert.Equal(expected, generated);
185+
186+
expected = Dictionary.Empty
187+
.Add("type_id", "Text")
188+
.Add("call", "Append")
189+
.Add("args", "Hello");
190+
generated = ActionBase.GeneratePlainValue<TextAction>(
191+
"Append", new Text("Hello"));
192+
Assert.Equal(expected, generated);
193+
194+
Assert.Contains(
195+
$"{nameof(ActionTypeAttribute)}",
196+
Assert.Throws<ArgumentException>(() =>
197+
ActionBase.GeneratePlainValue<InvalidAction>("Add", new Integer(5))).Message);
198+
Assert.Contains(
199+
$"cannot be found",
200+
Assert.Throws<ArgumentException>(() =>
201+
ActionBase.GeneratePlainValue<NumberAction>("Divide", new Integer(5))).Message);
202+
Assert.Contains(
203+
$"{nameof(ExecutableAttribute)}",
204+
Assert.Throws<ArgumentException>(() =>
205+
ActionBase.GeneratePlainValue<NumberAction>("DoNothing", new Integer(5))).Message);
206+
}
173207
}
174208
}

Libplanet.SDK.Action/Action/ActionBase.API.cs

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
11
using System.Reflection;
22
using Bencodex.Types;
3+
using Libplanet.Action;
34
using Libplanet.Crypto;
45
using Libplanet.SDK.Action.Attributes;
56

67
namespace Libplanet.SDK.Action
78
{
89
public partial class ActionBase
910
{
11+
public static IValue GeneratePlainValue<T>(string methodName, IValue args)
12+
where T : ActionBase
13+
{
14+
ActionTypeAttribute actionType = typeof(T).GetCustomAttribute<ActionTypeAttribute>() ??
15+
throw new ArgumentException(
16+
$"Type is missing a {nameof(ActionTypeAttribute)}.");
17+
18+
MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
19+
throw new ArgumentException(
20+
$"Method named {methodName} cannot be found for {typeof(T)}.",
21+
nameof(methodName));
22+
if (methodInfo.GetCustomAttribute<ExecutableAttribute>() is null)
23+
{
24+
throw new ArgumentException(
25+
$"Target method is missing a {nameof(ExecutableAttribute)}.",
26+
nameof(methodName));
27+
}
28+
29+
return Dictionary.Empty
30+
.Add("type_id", actionType.TypeIdentifier)
31+
.Add("call", methodName)
32+
.Add("args", args);
33+
}
34+
1035
protected IValue? GetState(Address address)
1136
=> World.GetAccount(StorageAddress).GetState(address);
1237

@@ -28,11 +53,12 @@ protected void Call<T>(string methodName, object?[]? args = null)
2853
calledAction.LoadContext(ActionContext, World);
2954

3055
MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
31-
throw new Exception("Method cannot be found.");
56+
throw new ArgumentException(
57+
$"Method named {methodName} cannot be found.");
3258
if (methodInfo.GetCustomAttribute<CallableAttribute>() is null)
3359
{
34-
throw new Exception(
35-
$"Target method is missing a {nameof(CallableAttribute)}");
60+
throw new ArgumentException(
61+
$"Target method {methodName} is missing a {nameof(CallableAttribute)}");
3662
}
3763

3864
methodInfo.Invoke(calledAction, args);
@@ -41,28 +67,36 @@ protected void Call<T>(string methodName, object?[]? args = null)
4167
_actionContext = calledAction._actionContext;
4268
}
4369

44-
protected U Call<T, U>(string methodName, object?[]? args = null)
45-
where T : ActionBase
70+
protected TR Call<TA, TR>(string methodName, object?[]? args = null)
71+
where TA : ActionBase
4672
{
47-
if (Activator.CreateInstance(typeof(T)) is not T calledAction)
73+
if (Activator.CreateInstance(typeof(TA)) is not TA calledAction)
4874
{
4975
throw new Exception("Action cannot be found.");
5076
}
5177

5278
calledAction.LoadContext(ActionContext, World);
5379

54-
MethodInfo methodInfo = typeof(T).GetMethod(methodName) ??
55-
throw new Exception("Method cannot be found.");
80+
MethodInfo methodInfo = typeof(TA).GetMethod(methodName) ??
81+
throw new ArgumentException(
82+
$"Method named {methodName} cannot be found.");
83+
if (methodInfo.GetCustomAttribute<CallableAttribute>() is null)
84+
{
85+
throw new ArgumentException(
86+
$"Target method {methodName} is missing a {nameof(CallableAttribute)}");
87+
}
5688

57-
if (methodInfo.Invoke(calledAction, args) is not U result)
89+
var result = methodInfo.Invoke(calledAction, args);
90+
if (result is not TR typedResult)
5891
{
59-
throw new Exception("Return type doesn't match.");
92+
throw new Exception(
93+
$"Return type is expected to be {typeof(TR)}: {result?.GetType()}");
6094
}
6195

6296
_world = calledAction._world;
6397
_actionContext = calledAction._actionContext;
6498

65-
return result;
99+
return typedResult;
66100
}
67101
}
68102
}

0 commit comments

Comments
 (0)