You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`Aggregated Contract` is the resultant response from all the aggregated apis. To create aggregated contract derive the class from `IContract` interface.
9
+
### Step 1. Initialise and use Parser class.
10
+
`Parser` is and implementation of `IParser` interface that provides methods for
11
+
- parsing content of a file by specifying the file path
12
+
- parsing an array of delimiter separated strings
11
13
12
-
Example.
14
+
Please see below.
13
15
```
14
-
public class Customer : IContract
15
-
{
16
-
public int Id { get; set; }
17
-
public string Code { get; set; }
18
-
public string Name { get; set; }
19
-
public Contacts Communication { get; set; }
20
-
public Order[] Orders { get; set; }
21
-
}
16
+
public interface IParser
17
+
{
18
+
public T[] Parse<T>(string filepath) where T : IFileLine, new();
19
+
public T[] Parse<T>(string[] lines) where T : IFileLine, new();
20
+
}
22
21
```
23
-
### Step 2. Create Api Aggregated
24
-
`Api Aggregate` is the composition of apis configured to obtain an aggregated data populated contract. To create an Api Aggregate derive from `ApiAggregate<TContract>` class where `TContract` is an implementation of IContract (ie. Agggregated Contract).
22
+
To initialise `Parser` class you could do it manually or use dependency injection as shown below. The parser class has parameterised constructor that takes the delimiter character to initialise the instance. Default character is ',' (comma) to initialise the parser for a CSV file parsing.
25
23
26
-
Example.
24
+
Example of Manual Instantiation is
27
25
```
28
-
internal class CustomerAggregate : ApiAggregate<Customer>
29
-
{
30
-
/// <summary>
31
-
/// Constructs the api aggregate with web apis and result transformers to map data to aggregated contract.
32
-
/// </summary>
33
-
/// <returns>Mappings</returns>
34
-
public override IEnumerable<Mapping<Customer, IApiResult>> Construct()
`Api Aggregate` comprises of apis configured in hierarchical nested graph with each api having an associated result transformers. The result from the api is fed to the associated result transformer to map data to the aggregated contract.
48
-
49
-
### 2.1 Api & Transformer Pair
50
-
Every `Api` type in the `ApiAggregate` definition should have a complementing `Transformer` type.
51
-
- You need to assign a `name` to the `api/transformer` pair. See below rules for api naming convention.
28
+
Example of IoC is
29
+
```
30
+
var services = new ServiceCollection();
52
31
53
-
Rules:
54
-
* You could nest api/transformer pairs in a `parent/child` hierarchy. In a given parent/child hierarchy, the output of the parent api will serve as the input to the nested api to resolve its api endpoint.
55
-
* The api/transformer mappings can be `nested` to `5` levels of dependency.
56
-
* By convention, The api name should be `dot` separated string with a dot for every nested level. The name should includes all parent names separated by a dot for a given hierarchy.
57
-
ie. For a 3 level dependency api mapping the name should be like `customer.orders.items`
32
+
services.AddTransient(typeof(IParser), c => new Parser(','));
33
+
// or use extension method
34
+
services.UseParsley('|');
58
35
59
-
Example.
60
-
> Below is the snippet from `CustomerAggregate` definition for parent/child relationship between Customer & Communication apis. The api response from CustomerApi is the input to CommunicationApi for resolving its endpoint url.
var parser = serviceProvider.GetService<IParser>();
61
38
```
62
-
.Map<CustomerApi, CustomerTransform>(With.Name("customer"), -- Parent mapping with name
63
-
customer => customer.Dependents
64
-
.Map<CommunicationApi, CommunicationTransform>(With.Name("customer.communication")) -- nested mapping with dot separated name
39
+
### Step 2. Define the `IFileLine` implementation to parse a file record into a strongly typed line class.
40
+
Consider the file below. To parse a row into a C# class, you need to implement `IFileLine` interface. By doing this you create a strongly typed line representation for each row in the file.
65
41
```
66
-
#### i. Web Api Class
67
-
The purpose of a api class is to execute the web api with api engine to fetch the response.
68
-
69
-
As mentioned previously, You can configure an api in `Parent` or `Child` (nested) mode in a hierarchical graph.
70
-
71
-
To create `Web Api` defined as `parent` or `nested` api, you need to implement from `WebApi<TResult>` class,
72
-
where `TResult` is `IApiResult` interface (or `ApiResult` base class) implementation and is the result that will be returned from executing the api.
42
+
|Mr|Jack Marias|Male|London|
43
+
|Dr|Bony Stringer|Male|New Jersey|
44
+
|Mrs|Mary Ward|Female||
45
+
|Mr|Robert Webb|||
46
+
```
47
+
Let us create an employee class which will hold data for each row shown in the file above. The properties in the line class should match to the column index and data type of the fields of the row.
73
48
74
-
Upon creating the web api class, you need to provide `GetUrl()` method implementation to return `Uri` instance.
75
-
* Implement the `GetUrl(IRequestContext context, IApiResult parentApiResult)` method to return the constructed endpoint using given parameters of the method.
76
-
* For `Parent Api`, only `IRequestContext` context parameter is passed to GetUrl() method to resolve the Url endpoint.
77
-
* For `Nested Api`, api result parameter (ie. `IApiResult` parentApiResult) from the parent api is additionally passed in to GetUrl() method along with IRequestContext context parameter.
78
-
* Optionally, override `GetRequestHeaders()` method to provide a dictionary of `outgoing headers` for the api request.
79
-
* Optionally, override `GetResponseHeaders()` method to provide any list of `incoming headers` from the api response.
80
-
*`IApiResult` implementation exposes `Headers` property for subscribed `response headers` received as part of the api response.
49
+
We use the column attribute to specify the column index and can optionally specify a default value for the associated column should it be be empty. As a rule of thumb, the number of properties with column attributes should match the number of columns in the row else the engine will throw an exception.
81
50
82
-
`Important:`
83
-
-The api `endpoint` needs to be resolved before executing the api with `ApiEngine`.
84
-
-`IApiResult` parentApiResult parameter is null for apis configured in parent mode.
51
+
IFileLine interface provides
52
+
-`Index` property that holds the index of the parsed line relative to the whole file,
53
+
-`Errors` property which is an array representing any column parsing failures.
85
54
86
-
Examples.
55
+
```
56
+
public interface IFileLine
57
+
{
58
+
public int Index { get; set; }
59
+
public IList<string> Errors { get; set; }
60
+
}
61
+
```
87
62
88
-
> See example `CustomerApi` implemented to be configured and run in parent mode.
89
-
```
90
-
public class CustomerApi : WebApi<CustomerResult>
63
+
Example.
64
+
```
65
+
public class Employee : IFileLine
91
66
{
92
-
public CustomerApi() : base(Endpoints.BaseAddress)
93
-
{
94
-
}
67
+
// Custom column properties
95
68
96
-
// Override to construct the api endpoint.
97
-
protected override Uri GetUrl(IRequestContext context, IApiResult parentApiResult)
98
-
{
99
-
// Executes as root or level 1 api. parentApiResult should be null.
100
-
var customerContext = (CustomerContext)context;
69
+
[Column(0)]
70
+
public string Title { get; set; }
71
+
[Column(1)]
72
+
public string Name { get; set; }
73
+
[Column(2)]
74
+
public EnumGender Sex { get; set; }
75
+
[Column(3, "London")]
76
+
public string Location { get; set; }
101
77
102
-
return new Uri(string.Format(Endpoints.Customer, customerContext.CustomerId));
103
-
}
78
+
// IFileLine Members
79
+
public int Index { get; set; }
80
+
public IList<string> Errors { get; set; }
81
+
}
82
+
```
83
+
Once you have created the line class it is as simple as calling one of the parser.Parse() methods below
104
84
105
-
// Override to pass custom outgoing headers with the api request.
var records = new Parser('|').Parse<Employee>(lines);
98
+
```
99
+
### Step 3. Advanced Parsing of data using nested types in the FileLine class.
100
+
You could implement advance parsing of data by implementing `TypeConverter` class. Suppose we have to change the Name string property in Employee class above to a `NameType` property shown below.
101
+
```
102
+
public class Employee : IFileLine
103
+
{
104
+
[Column(0)]
105
+
public string Title { get; set; }
106
+
[Column(1)]
107
+
public NameType Name { get; set; }
108
+
[Column(2)]
109
+
public EnumGender Sex { get; set; }
110
+
[Column(3, "London")]
111
+
public string Location { get; set; }
112
+
113
+
// IFileLine Members
114
+
public int Index { get; set; }
115
+
public IList<string> Errors { get; set; }
116
+
}
117
+
118
+
public class NameType
119
+
{
120
+
public string FirstName { get; set; }
121
+
public string Surname { get; set; }
119
122
}
120
123
```
121
124
122
-
> See example `CommunicationApi` implemented to be configured and run as nested api to customer api below.
125
+
In order to parse the string name value from delimiter separated record in the file correctly to NameType instance, you need to implement custom `TypeConverter` converter.
126
+
127
+
Example - `NameConverter`
123
128
```
124
-
internal class CommunicationApi : WebApi<CommunicationResult>
129
+
public class NameConverter : TypeConverter
130
+
{
131
+
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
125
132
{
126
-
public CommunicationApi() : base(Endpoints.BaseAddress)
127
-
{
128
-
}
129
-
130
-
protected override Uri GetUrl(IRequestContext context, IApiResult parentApiResult)
131
-
{
132
-
var customer = (CustomerResult)parentApiResult;
133
-
return new Uri(string.Format(Endpoints.Communication, customer.Id));
Note: Above examples to enable HttpClient is basic. However, you could additionally implement circuit breaking & retry policies.
200
-
>Please see [`Circuit Breaker`](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-circuit-breaker-pattern) pattern for more details.
201
-
202
-
203
-
### Step 4. Use IParsley.Net<`TContract`>
204
-
205
-
#### i. IApiAggrgator (DI)
206
-
To use Api aggregator, Inject IApiAggrgator<TContract> where TContract is IContract, using constructor & property injection method or explicity Resolve using service provider
Now parsing the file should hydrate data correctly to the FileLine class and its nested name type.
209
186
210
-
#### ii. Call Aggregator.GetData() method
211
-
You need to call the `GetData()` method with an instance of parameter class derived from `IRequestContext` interface.
212
-
- The IRequestContext provides a `Names` property which is a list of string to include all the api names to be included for the given request to fetch aggregated response.
213
-
- When `no` names are passed in the paramter then `entire` aggregated response for all configured apis is returned.
214
-
- When `subset` of apis are included using names then the returned aggregated response only includes `api responses` from included apis.
215
-
- When nested api with `dot` separated api name (eg. `customer.orders.items`) is included then all parent apis also get included for the dependency.
216
-
217
-
##### Example - Control Flow
218
-
> Example execution flow for a nested api included in the GetData() parameter.
0 commit comments