Skip to content

Commit 8558a0e

Browse files
committed
Basic prototype
1 parent 762bb4f commit 8558a0e

File tree

49 files changed

+1437
-212
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1437
-212
lines changed

src/Examples/DapperExample/Repositories/DapperRepository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,9 @@ private async Task ApplyTargetedFieldsAsync(TResource resourceFromRequest, TReso
252252
relationship.SetValue(resourceInDatabase, rightValueEvaluated);
253253
}
254254

255-
foreach (AttrAttribute attribute in _targetedFields.Attributes)
255+
foreach (ITargetedAttributeTree target in _targetedFields.Attributes)
256256
{
257-
attribute.SetValue(resourceInDatabase, attribute.GetValue(resourceFromRequest));
257+
target.Apply(resourceFromRequest, resourceInDatabase);
258258
}
259259
}
260260

src/Examples/GettingStarted/Data/SampleDbContext.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,30 @@
22
using JetBrains.Annotations;
33
using Microsoft.EntityFrameworkCore;
44

5+
// @formatter:wrap_chained_method_calls chop_always
6+
57
namespace GettingStarted.Data;
68

79
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
810
public class SampleDbContext(DbContextOptions<SampleDbContext> options)
911
: DbContext(options)
1012
{
1113
public DbSet<Book> Books => Set<Book>();
14+
15+
protected override void OnModelCreating(ModelBuilder builder)
16+
{
17+
ArgumentNullException.ThrowIfNull(builder);
18+
19+
builder.Entity<Person>()
20+
.OwnsOne(person => person.LivingAddress)
21+
.ToJson();
22+
23+
builder.Entity<Person>()
24+
.OwnsOne(person => person.MailAddress)
25+
.ToJson();
26+
27+
builder.Entity<Person>()
28+
.OwnsMany(person => person.Addresses)
29+
.ToJson();
30+
}
1231
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
@hostname=localhost
2+
@port=14141
3+
4+
### Get all
5+
6+
GET http://{{hostname}}:{{port}}/api/people
7+
8+
### Get all with include
9+
10+
GET http://{{hostname}}:{{port}}/api/people?include=books
11+
12+
### Filter inside compound attribute
13+
14+
GET http://{{hostname}}:{{port}}/api/people?filter=startsWith(livingAddress.country,'T')
15+
16+
### TODO: Test filters descending into compound attribute (requires an expression that has child scope: "has").
17+
### TODO: Consider supporting to-many filters on collections: "has", "any", "count"
18+
### Requires to act on parent Attr instead of ResourceType, which likely requires major breaking change.
19+
20+
21+
### Patch string collection attribute
22+
23+
PATCH http://{{hostname}}:{{port}}/api/people/1
24+
Content-Type: application/vnd.api+json
25+
26+
{
27+
"data": {
28+
"type": "people",
29+
"id": "1",
30+
"attributes": {
31+
"namesOfChildren": ["Mary", "Ann", null]
32+
}
33+
}
34+
}
35+
36+
### Patch int collection attribute
37+
38+
PATCH http://{{hostname}}:{{port}}/api/people/1
39+
Content-Type: application/vnd.api+json
40+
41+
{
42+
"data": {
43+
"type": "people",
44+
"id": "1",
45+
"attributes": {
46+
"agesOfChildren": [15, 25, null]
47+
}
48+
}
49+
}
50+
51+
### Patch members of compound attribute
52+
53+
PATCH http://{{hostname}}:{{port}}/api/people/1
54+
Content-Type: application/vnd.api+json
55+
56+
{
57+
"data": {
58+
"type": "people",
59+
"id": "1",
60+
"attributes": {
61+
"livingAddress": {
62+
"country": "Germany",
63+
"street": "OtherStreet"
64+
}
65+
}
66+
}
67+
}
68+
69+
### Patch members of compound attribute, setting them to null
70+
71+
PATCH http://{{hostname}}:{{port}}/api/people/1
72+
Content-Type: application/vnd.api+json
73+
74+
{
75+
"data": {
76+
"type": "people",
77+
"id": "1",
78+
"attributes": {
79+
"livingAddress": {
80+
"country": null,
81+
"street": null
82+
}
83+
}
84+
}
85+
}
86+
87+
### Patch compound attribute to empty object (must be a no-op)
88+
89+
PATCH http://{{hostname}}:{{port}}/api/people/1
90+
Content-Type: application/vnd.api+json
91+
92+
{
93+
"data": {
94+
"type": "people",
95+
"id": "1",
96+
"attributes": {
97+
"mailAddress": {}
98+
}
99+
}
100+
}
101+
102+
### Patch compound attribute to null
103+
104+
PATCH http://{{hostname}}:{{port}}/api/people/1
105+
Content-Type: application/vnd.api+json
106+
107+
{
108+
"data": {
109+
"type": "people",
110+
"id": "1",
111+
"attributes": {
112+
"mailAddress": null
113+
}
114+
}
115+
}
116+
117+
### Patch compound nullable collection attribute (null element is blocked by EF Core)
118+
119+
PATCH http://{{hostname}}:{{port}}/api/people/1
120+
Content-Type: application/vnd.api+json
121+
122+
{
123+
"data": {
124+
"type": "people",
125+
"id": "1",
126+
"attributes": {
127+
"addresses": [{
128+
"country": "Germany"
129+
}, {
130+
"street": "SomeStreet"
131+
}, {
132+
}
133+
]
134+
}
135+
}
136+
}
137+
138+
### Patch relationships to null - SHOULD THIS FAIL OR NO-OP?
139+
140+
PATCH http://{{hostname}}:{{port}}/api/people/1
141+
Content-Type: application/vnd.api+json
142+
143+
{
144+
"data": {
145+
"type": "people",
146+
"id": "1",
147+
"relationships": null
148+
}
149+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using JsonApiDotNetCore.Resources.Annotations;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace GettingStarted.Models;
5+
6+
[Owned]
7+
public sealed class Address
8+
{
9+
[Attr]
10+
public string? Street { get; set; }
11+
12+
[Attr]
13+
public string? PostalCode { get; set; }
14+
15+
[Attr]
16+
public string? Country { get; set; }
17+
18+
public string? NotExposed { get; set; }
19+
}

src/Examples/GettingStarted/Models/Person.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,26 @@ namespace GettingStarted.Models;
88
[Resource]
99
public sealed class Person : Identifiable<int>
1010
{
11+
// TODO: See if we can use "required" throughout the codebase, instead of null! suppression.
12+
13+
[Attr]
14+
public required string Name { get; set; }
15+
16+
[Attr(IsCompound = true)]
17+
public required Address LivingAddress { get; set; }
18+
19+
[Attr(IsCompound = true)]
20+
public Address? MailAddress { get; set; }
21+
22+
// OwnsMany with nullable element type is unsupported by EF Core.
23+
[Attr(IsCompound = true)]
24+
public List<Address>? Addresses { get; set; }
25+
26+
[Attr]
27+
public List<string?> NamesOfChildren { get; set; } = [];
28+
1129
[Attr]
12-
public string Name { get; set; } = null!;
30+
public List<int?> AgesOfChildren { get; set; } = [];
1331

1432
[HasMany]
1533
public ICollection<Book> Books { get; set; } = new List<Book>();

src/Examples/GettingStarted/Program.cs

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,77 @@ static async Task CreateSampleDataAsync(SampleDbContext dbContext)
6464
// Note: The generate-examples.ps1 script (to create example requests in documentation) depends on these.
6565

6666
dbContext.Books.AddRange(new Book
67-
{
68-
Title = "Frankenstein",
69-
PublishYear = 1818,
70-
Author = new Person
7167
{
72-
Name = "Mary Shelley"
68+
Title = "Frankenstein",
69+
PublishYear = 1818,
70+
Author = new Person
71+
{
72+
Name = "Mary Shelley",
73+
LivingAddress = new Address
74+
{
75+
Street = "SomeStreet",
76+
PostalCode = "1234 AB",
77+
Country = "The Netherlands",
78+
NotExposed = "NotExposed"
79+
},
80+
MailAddress = new Address
81+
{
82+
Street = "MailStreet",
83+
PostalCode = "MailPostalCode",
84+
Country = "MailCountry",
85+
NotExposed = "MailNotExposed"
86+
},
87+
Addresses =
88+
[
89+
new Address
90+
{
91+
Street = "Street1",
92+
PostalCode = "PostalCode1",
93+
Country = "Country1",
94+
NotExposed = "NotExposed1"
95+
},
96+
new Address
97+
{
98+
Street = "Street2",
99+
PostalCode = "PostalCode2",
100+
Country = "Country2",
101+
NotExposed = "NotExposed2"
102+
}
103+
],
104+
NamesOfChildren =
105+
[
106+
"John",
107+
"Jack",
108+
"Joe",
109+
null
110+
],
111+
AgesOfChildren =
112+
[
113+
10,
114+
20,
115+
30,
116+
null
117+
]
118+
}
73119
}
74-
}, new Book
75-
{
76-
Title = "Robinson Crusoe",
77-
PublishYear = 1719,
78-
Author = new Person
120+
/*, new Book
79121
{
80-
Name = "Daniel Defoe"
81-
}
82-
}, new Book
83-
{
84-
Title = "Gulliver's Travels",
85-
PublishYear = 1726,
86-
Author = new Person
122+
Title = "Robinson Crusoe",
123+
PublishYear = 1719,
124+
Author = new Person
125+
{
126+
Name = "Daniel Defoe"
127+
}
128+
}, new Book
87129
{
88-
Name = "Jonathan Swift"
89-
}
90-
});
130+
Title = "Gulliver's Travels",
131+
PublishYear = 1726,
132+
Author = new Person
133+
{
134+
Name = "Jonathan Swift"
135+
}
136+
}*/
137+
);
91138

92139
await dbContext.SaveChangesAsync();
93140
}

src/Examples/GettingStarted/Properties/launchSettings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
"profiles": {
1111
"IIS Express": {
1212
"commandName": "IISExpress",
13-
"launchBrowser": true,
13+
"launchBrowser": false,
1414
"launchUrl": "api/people?include=books",
1515
"environmentVariables": {
1616
"ASPNETCORE_ENVIRONMENT": "Development"
1717
}
1818
},
1919
"Kestrel": {
2020
"commandName": "Project",
21-
"launchBrowser": true,
21+
"launchBrowser": false,
2222
"launchUrl": "api/people?include=books",
2323
"applicationUrl": "http://localhost:14141",
2424
"environmentVariables": {

0 commit comments

Comments
 (0)