Skip to content

Commit 0d7acca

Browse files
Fixed internal server error for REST services with string collection output parameters. (#1032)
* Fixed internal server error for REST services with string collection output parameters. * Fix a broken case and add it to unit tests. * Remove Debugger. * Fix broken case with rest services returning SDT collection. * Simple Collections do not need to convert. * Add test for rest service returning SDT collection. (cherry picked from commit 3495b86)
1 parent a50fb1d commit 0d7acca

File tree

12 files changed

+2609
-11
lines changed

12 files changed

+2609
-11
lines changed

dotnet/src/dotnetframework/GxClasses/Services/GxRestWrapper.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -888,21 +888,31 @@ protected static object MakeRestType( object collectionValue, bool isApiObject)
888888
{
889889
restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTLInterface", null);
890890
}
891-
if (restItemType == null)//Collection<SDTType> convert to GxGenericCollection<SDTType_RESTInterface>
891+
else //Collection<SDTType> convert to GxGenericCollection<SDTType_RESTInterface>
892892
{
893-
restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null);
893+
if (typeof(GxUserType).IsAssignableFrom(itemType))
894+
{
895+
restItemType = ClassLoader.FindType(Config.CommonAssemblyName, itemType.FullName + "_RESTInterface", null);
896+
}
897+
else
898+
{
899+
collectionObject = collectionValue;
900+
}
894901
}
895-
object[] attributes = restItemType.GetCustomAttributes(typeof(GxJsonSerialization), false);
896-
IEnumerable<object> serializationAttributes = attributes.Where(a => a.GetType() == typeof(GxJsonSerialization));
897-
if (serializationAttributes != null && serializationAttributes.Any<object>())
902+
if (restItemType != null)
898903
{
899-
GxJsonSerialization attFmt = (GxJsonSerialization)serializationAttributes.FirstOrDefault();
900-
wrappedStatus = attFmt.JsonUnwrapped;
901-
isWrapped = (isApiObject)? ((wrappedStatus == "wrapped")? true: false): ((wrappedStatus == "unwrapped") ? false : true);
904+
object[] attributes = restItemType.GetCustomAttributes(typeof(GxJsonSerialization), false);
905+
IEnumerable<object> serializationAttributes = attributes.Where(a => a.GetType() == typeof(GxJsonSerialization));
906+
if (serializationAttributes != null && serializationAttributes.Any<object>())
907+
{
908+
GxJsonSerialization attFmt = (GxJsonSerialization)serializationAttributes.FirstOrDefault();
909+
wrappedStatus = attFmt.JsonUnwrapped;
910+
isWrapped = (isApiObject) ? ((wrappedStatus == "wrapped") ? true : false) : ((wrappedStatus == "unwrapped") ? false : true);
911+
}
912+
bool isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false);
913+
Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType);
914+
collectionObject = Activator.CreateInstance(genericListItemType, new object[] { collectionValue, isWrapped, wrappedStatus });
902915
}
903-
bool isEmpty = !restItemType.IsDefined(typeof(GxOmitEmptyCollection), false);
904-
Type genericListItemType = typeof(GxGenericCollection<>).MakeGenericType(restItemType);
905-
collectionObject = Activator.CreateInstance(genericListItemType, new object[] { collectionValue, isWrapped , wrappedStatus});
906916
// Empty collection serialized w/ noproperty
907917
if (collectionObject is IList restList)
908918
{

dotnet/test/DotNetCoreWebUnitTest/DotNetCoreWebUnitTest.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@
5757
<None Update="apps\createsession.svc">
5858
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
5959
</None>
60+
<None Update="apps\getcollection.svc">
61+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
62+
</None>
63+
<None Update="apps\getsdtcollection.svc">
64+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
65+
</None>
66+
<None Update="apps\getbccollection.svc">
67+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
68+
</None>
6069
<None Update="apps\httpcors.svc">
6170
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
6271
</None>

dotnet/test/DotNetCoreWebUnitTest/Middleware/RestServiceTest.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ public RestServiceTest() : base()
2323
{
2424
ClassLoader.FindType("apps.append", "GeneXus.Programs.apps", "append", Assembly.GetExecutingAssembly(), true);//Force loading assembly for append procedure
2525
ClassLoader.FindType("apps.saveimage", "GeneXus.Programs.apps", "saveimage", Assembly.GetExecutingAssembly(), true);//Force loading assembly for saveimage procedure
26+
ClassLoader.FindType("apps.getcollection", "GeneXus.Programs.apps", "getcollection", Assembly.GetExecutingAssembly(), true);
27+
ClassLoader.FindType("apps.getsdtcollection", "GeneXus.Programs.apps", "getsdtcollection", Assembly.GetExecutingAssembly(), true);
28+
ClassLoader.FindType("apps.getbccollection", "GeneXus.Programs.apps", "getbccollection", Assembly.GetExecutingAssembly(), true);
2629
server.AllowSynchronousIO = true;
2730
}
2831
const string serviceBodyResponse = "OK";
@@ -129,6 +132,7 @@ private async Task<HttpResponseMessage> RunController(HttpClient client)
129132
return response;
130133
}
131134
string ACCESS_CONTROL_MAX_AGE_HEADER = "86400";
135+
132136
[Fact]
133137
public async Task TestHttpResponseOnRestService()
134138
{
@@ -140,6 +144,44 @@ public async Task TestHttpResponseOnRestService()
140144
Assert.Equal(ACCESS_CONTROL_MAX_AGE_HEADER, values.FirstOrDefault());
141145
}
142146

147+
[Fact]
148+
public async Task TestRestServiceWithSimpleCollectionOutput()
149+
{
150+
server.AllowSynchronousIO = true;
151+
HttpClient client = server.CreateClient();
152+
HttpResponseMessage response = await client.PostAsync("rest/apps/getcollection", null);
153+
response.EnsureSuccessStatusCode();
154+
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
155+
string responseBody = await response.Content.ReadAsStringAsync();
156+
Assert.Equal("{\"CliType\":1,\"CliCode\":[1,2]}", responseBody);
157+
}
158+
[Fact]
159+
public async Task TestRestServiceWithBCCollectionOutput()
160+
{
161+
server.AllowSynchronousIO = true;
162+
HttpClient client = server.CreateClient();
163+
HttpResponseMessage response = await client.PostAsync("rest/apps/getbccollection", null);
164+
response.EnsureSuccessStatusCode();
165+
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
166+
string responseBody = await response.Content.ReadAsStringAsync();
167+
168+
string expected = "{\"InvoiceDate\":\"2024-02-02\",\"uri\":\"\"}";
169+
Assert.Contains(expected, responseBody, StringComparison.OrdinalIgnoreCase);
170+
}
171+
[Fact]
172+
public async Task TestRestServiceWithSdtCollectionOutput()
173+
{
174+
server.AllowSynchronousIO = true;
175+
HttpClient client = server.CreateClient();
176+
HttpResponseMessage response = await client.PostAsync("rest/apps/getsdtcollection", null);
177+
response.EnsureSuccessStatusCode();
178+
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
179+
string responseBody = await response.Content.ReadAsStringAsync();
180+
181+
string expected = "{\"CustomerId\":1,";
182+
Assert.Contains(expected, responseBody, StringComparison.OrdinalIgnoreCase);
183+
}
184+
143185
}
144186

145187
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
using System;
2+
using GeneXus.Application;
3+
using GeneXus.Data.NTier;
4+
using GeneXus.Procedure;
5+
using GeneXus.Utils;
6+
namespace GeneXus.Programs.apps
7+
{
8+
public class getbccollection : GXProcedure
9+
{
10+
public getbccollection()
11+
{
12+
context = new GxContext();
13+
DataStoreUtil.LoadDataStores(context);
14+
IsMain = true;
15+
context.SetDefaultTheme("GeneXusXEv2", false);
16+
}
17+
18+
public getbccollection(IGxContext context)
19+
{
20+
this.context = context;
21+
IsMain = false;
22+
}
23+
24+
public void execute(DateTime aP0_invoicedate,
25+
short aP1_CustomerId,
26+
string aP2_Customername,
27+
out GXBCCollection<SdtInvoice> aP3_Gxm2rootcol)
28+
{
29+
this.AV6invoicedate = aP0_invoicedate;
30+
this.AV5CustomerId = aP1_CustomerId;
31+
this.AV7Customername = aP2_Customername;
32+
this.Gxm2rootcol = new GXBCCollection<SdtInvoice>(context, "Invoice", "TestRestProcs");
33+
initialize();
34+
ExecuteImpl();
35+
aP3_Gxm2rootcol = this.Gxm2rootcol;
36+
}
37+
38+
public GXBCCollection<SdtInvoice> executeUdp(DateTime aP0_invoicedate,
39+
short aP1_CustomerId,
40+
string aP2_Customername)
41+
{
42+
execute(aP0_invoicedate, aP1_CustomerId, aP2_Customername, out aP3_Gxm2rootcol);
43+
return Gxm2rootcol;
44+
}
45+
46+
public void executeSubmit(DateTime aP0_invoicedate,
47+
short aP1_CustomerId,
48+
string aP2_Customername,
49+
out GXBCCollection<SdtInvoice> aP3_Gxm2rootcol)
50+
{
51+
this.AV6invoicedate = aP0_invoicedate;
52+
this.AV5CustomerId = aP1_CustomerId;
53+
this.AV7Customername = aP2_Customername;
54+
this.Gxm2rootcol = new GXBCCollection<SdtInvoice>(context, "Invoice", "TestRestProcs");
55+
SubmitImpl();
56+
aP3_Gxm2rootcol = this.Gxm2rootcol;
57+
}
58+
59+
protected override void ExecutePrivate()
60+
{
61+
/* GeneXus formulas */
62+
/* Output device settings */
63+
Gxm1invoice = new SdtInvoice(context);
64+
Gxm2rootcol.Add(Gxm1invoice, 0);
65+
Gxm1invoice.gxTpr_Invoiceid = 1;
66+
Gxm1invoice.gxTpr_Invoicedate = context.localUtil.YMDToD(2024, 1, 1);
67+
Gxm1invoice.gxTpr_Customerid = 1;
68+
Gxm3invoice_level = new SdtInvoice_Level(context);
69+
Gxm1invoice.gxTpr_Level.Add(Gxm3invoice_level, 0);
70+
Gxm3invoice_level.gxTpr_Invoicelevelid = 1;
71+
Gxm3invoice_level.gxTpr_Productid = 1;
72+
Gxm3invoice_level.gxTpr_Invoicelevelqty = 10;
73+
74+
75+
Gxm1invoice = new SdtInvoice(context);
76+
Gxm2rootcol.Add(Gxm1invoice, 0);
77+
Gxm1invoice.gxTpr_Invoiceid = 2;
78+
Gxm1invoice.gxTpr_Invoicedate = context.localUtil.YMDToD(2024, 2, 2);
79+
Gxm1invoice.gxTpr_Customerid = 2;
80+
Gxm3invoice_level = new SdtInvoice_Level(context);
81+
Gxm1invoice.gxTpr_Level.Add(Gxm3invoice_level, 0);
82+
Gxm3invoice_level.gxTpr_Invoicelevelid = 2;
83+
Gxm3invoice_level.gxTpr_Productid = 2;
84+
Gxm3invoice_level.gxTpr_Invoicelevelqty = 20;
85+
cleanup();
86+
}
87+
88+
public override void cleanup()
89+
{
90+
CloseCursors();
91+
if (IsMain)
92+
{
93+
context.CloseConnections();
94+
}
95+
ExitApp();
96+
}
97+
98+
public override void initialize()
99+
{
100+
Gxm1invoice = new SdtInvoice(context);
101+
Gxm3invoice_level = new SdtInvoice_Level(context);
102+
/* GeneXus formulas. */
103+
}
104+
105+
private short AV5CustomerId;
106+
private string AV7Customername;
107+
private DateTime AV6invoicedate;
108+
private GXBCCollection<SdtInvoice> Gxm2rootcol;
109+
private SdtInvoice Gxm1invoice;
110+
private SdtInvoice_Level Gxm3invoice_level;
111+
private GXBCCollection<SdtInvoice> aP3_Gxm2rootcol;
112+
}
113+
114+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%@ServiceHost Service= "GeneXus.Programs.apps.getbccollection,getbccollection" %>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using GeneXus.Application;
2+
using GeneXus.Data.NTier;
3+
using GeneXus.Procedure;
4+
using GeneXus.Utils;
5+
namespace GeneXus.Programs.apps
6+
{
7+
public class getcollection : GXProcedure
8+
{
9+
public getcollection( )
10+
{
11+
context = new GxContext( );
12+
DataStoreUtil.LoadDataStores( context);
13+
IsMain = true;
14+
context.SetDefaultTheme("FromString", true);
15+
}
16+
17+
public getcollection( IGxContext context )
18+
{
19+
this.context = context;
20+
IsMain = false;
21+
}
22+
23+
public void execute( out short aP0_CliType ,
24+
out GxSimpleCollection<int> aP1_CliCode )
25+
{
26+
this.clitype = 0 ;
27+
this.cliCod = new GxSimpleCollection<int>() ;
28+
initialize();
29+
ExecuteImpl();
30+
aP0_CliType=this.clitype;
31+
aP1_CliCode=this.cliCod;
32+
}
33+
34+
public GxSimpleCollection<int> executeUdp( out short aP0_CliType )
35+
{
36+
execute(out aP0_CliType, out aP1_CliCode);
37+
return cliCod ;
38+
}
39+
40+
public void executeSubmit( out short aP0_CliType ,
41+
out GxSimpleCollection<int> aP1_CliCode )
42+
{
43+
this.clitype = 0 ;
44+
this.cliCod = new GxSimpleCollection<int>() ;
45+
SubmitImpl();
46+
aP0_CliType=this.clitype;
47+
aP1_CliCode=this.cliCod;
48+
}
49+
50+
protected override void ExecutePrivate( )
51+
{
52+
/* GeneXus formulas */
53+
/* Output device settings */
54+
clitype = 1;
55+
cliCod.Add(1, 0);
56+
cliCod.Add(2, 0);
57+
this.cleanup();
58+
}
59+
60+
public override void cleanup( )
61+
{
62+
CloseCursors();
63+
if ( IsMain )
64+
{
65+
context.CloseConnections();
66+
}
67+
ExitApp();
68+
}
69+
70+
public override void initialize( )
71+
{
72+
cliCod = new GxSimpleCollection<int>();
73+
74+
}
75+
76+
private short clitype ;
77+
private GxSimpleCollection<int> cliCod ;
78+
private GxSimpleCollection<int> aP1_CliCode ;
79+
}
80+
81+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%@ServiceHost Service= "GeneXus.Programs.apps.getcollection,apps.getcollection" %>

0 commit comments

Comments
 (0)