Skip to content

v6_0_0_how_to

moh-hassan edited this page Jun 26, 2023 · 2 revisions

OData2Poco Version 6.0.0

OData2Poco v6.0.0 include many valuable new fatures and enhancement. You can download the packages from nuget site. OData2Poco is cross-platform support Windows, macOS, and Linux.

What’s New in v6.0.0:

Code Generation

Security

User Experience

Http Connection


A new powerful option to dynamically generate attributes for c# classes and properties using a simple text file

A new powerful option --att-defs allows you to dynamically generate attributes for c# classes and properties using a simple text file that contains your template with expressions. These expressions are valid C# code that can utilize C# string functions and other built-in extension methods. You can also filter on classes and properties to apply the attributes selectively.

To use the feature:

  • Add the following option to the commandLine:
--att-defs path/to/file
  • Create a text file (ini format) with the minimal code as given below:
# the attribute for all properties only (exclude classes).
[myJson]
 Format=[JsonProperty ("{{PropName.ToCamelCase()}}")]

# the attribute for all classes only (exclude properties)
[map]
  Scope= class
  Format= [AdaptTo("[name]Dto")]

The above file, define an attribute named 'myJson' with the Format entry which will be applied to all properties of the class.

The expression is evaluated for every property / class. The Format is the syntax of the attribute with the expressions enclosed between {{...}}. The expression {{PropName.ToCamelCase().Quote()}} is evaluated with the actual value of PropName with applying the built-in extension methods in OData2Poco 'ToCamelCase()'.

The name of the attribute is the section name, e.g [myJson]

The result is like:

   [AdaptTo("[name]Dto")]
public partial class Manager : Person
	{
	    [JsonProperty ("budget")]
	    public long Budget {get;set;} // not null

	    [JsonProperty ("bossOffice")]
	    public Location BossOffice {get;set;} 

	}

As you see, the 'budget' is the camelCase of the name of the property 'Budget'

Format of the attribute file

The file is ini-format and it's composed of following entries:

  • Name of attribute (section name) which is enclosed between brackets [...]

  • The Scope with a lowercase value: 'property' or 'class'. The default value is 'property' and can be be dropped.

    • For property scope, attribute is applied for Properties only.
    • For class scope, attribute is applied for classes only.
  • The Format (mandatory entry): is the syntax of the attribute with expressions enclosed between {{...}}.

  • The Filter (an optional entry). It is a valid c# expression of one c# expression code. The expression can reference any property in the class PropertyTemplate or ClassTemplate

Notes:

  • The line start with # is a comment and it's ognored by the parser.

  • Attributes without Format will be ignored, but the cli tool display a warning message.

  • The expression is valid C# code that can utilize C# string functions and other built-in extension methods. You can also filter on classes and properties to apply the attributes selectively.

  • Filter can be on more than one line.

[attribute_name]
#Scope can be dropped for properties
Scope= class
Format=[DataContract]
#apply only on classes the start with 'A'
Filter= ClassName.StartsWith('A')

Complete demo Example:

file: attributes.txt

# file Name: attributes.txt
 [map]
  Scope= class
  Format= [AdaptTo("[name]Dto")]
  #apply attribute on classes that have the condition Name StartsWith("A") 
  Filter= Name.StartsWith("A")

  [json4]
  Format=[JsonProperty ("{{PropName.ToCamelCase()}}")]

The commandLine:

o2pgen -r https://services.odata.org/TripPinRESTierService ^
 --att-defs attributes.txt -a map json4

Output result: Note: only classes Airline and Airport have attribute and all properties with jsonProperty camelCase

Click to expand!
namespace Trippin
{
	// EntitySetName: People
	public partial class Person
	{
	    [JsonProperty ("userName")]
	    public string UserName {get;set;} //PrimaryKey not null

	    [JsonProperty ("firstName")]
	    public string FirstName {get;set;} // not null

	    [JsonProperty ("lastName")]
	    public string LastName {get;set;} 

	    [JsonProperty ("middleName")]
	    public string MiddleName {get;set;} 

	    [JsonProperty ("gender")]
	    public PersonGender Gender {get;set;} // not null

	    [JsonProperty ("age")]
	    public long Age {get;set;} 

	    [JsonProperty ("emails")]
	    public List<string> Emails {get;set;} 

	    [JsonProperty ("addressInfo")]
	    public List<Location> AddressInfo {get;set;} 

	    [JsonProperty ("homeAddress")]
	    public Location HomeAddress {get;set;} 

	    [JsonProperty ("favoriteFeature")]
	    public Feature FavoriteFeature {get;set;} // not null

	    [JsonProperty ("features")]
	    public List<Feature> Features {get;set;} 

	}

	// EntitySetName: Airlines
	[AdaptTo("[name]Dto")]
	public partial class Airline
	{
	    [JsonProperty ("airlineCode")]
	    public string AirlineCode {get;set;} //PrimaryKey not null

	    [JsonProperty ("name")]
	    public string Name {get;set;} 

	}

	// EntitySetName: Airports
	[AdaptTo("[name]Dto")]
	public partial class Airport
	{
	    [JsonProperty ("name")]
	    public string Name {get;set;} 

	    [JsonProperty ("icaoCode")]
	    public string IcaoCode {get;set;} //PrimaryKey not null

	    [JsonProperty ("iataCode")]
	    public string IataCode {get;set;} 

	    [JsonProperty ("location")]
	    public AirportLocation Location {get;set;} 

	}

	// Complex Entity
	public partial class Location
	{
	    [JsonProperty ("address")]
	    public string Address {get;set;} 

	    [JsonProperty ("city")]
	    public City City {get;set;} 

	}

	// Complex Entity
	public partial class City
	{
	    [JsonProperty ("name")]
	    public string Name {get;set;} 

	    [JsonProperty ("countryRegion")]
	    public string CountryRegion {get;set;} 

	    [JsonProperty ("region")]
	    public string Region {get;set;} 

	}

	// Complex Entity
	[AdaptTo("[name]Dto")]
	public partial class AirportLocation : Location
	{
	    [JsonProperty ("loc")]
	    public GeographyPoint Loc {get;set;} 

	}

	// Complex Entity
	public partial class EventLocation : Location
	{
	    [JsonProperty ("buildingInfo")]
	    public string BuildingInfo {get;set;} 

	}

	// EntitySetName: Trip
	public partial class Trip
	{
	    [JsonProperty ("tripId")]
	    public int TripId {get;set;} //PrimaryKey not null

	    [JsonProperty ("shareId")]
	    public Guid ShareId {get;set;} // not null

	    [JsonProperty ("name")]
	    public string Name {get;set;} 

	    [JsonProperty ("budget")]
	    public float Budget {get;set;} // not null

	    [JsonProperty ("description")]
	    public string Description {get;set;} 

	    [JsonProperty ("tags")]
	    public List<string> Tags {get;set;} 

	    [JsonProperty ("startsAt")]
	    public DateTimeOffset StartsAt {get;set;} // not null

	    [JsonProperty ("endsAt")]
	    public DateTimeOffset EndsAt {get;set;} // not null

	}

	// EntitySetName: PlanItem
	public partial class PlanItem
	{
	    [JsonProperty ("planItemId")]
	    public int PlanItemId {get;set;} //PrimaryKey not null

	    [JsonProperty ("confirmationCode")]
	    public string ConfirmationCode {get;set;} 

	    [JsonProperty ("startsAt")]
	    public DateTimeOffset StartsAt {get;set;} // not null

	    [JsonProperty ("endsAt")]
	    public DateTimeOffset EndsAt {get;set;} // not null

	    [JsonProperty ("duration")]
	    public TimeSpan Duration {get;set;} // not null

	}

	// EntitySetName: Event
	public partial class Event : PlanItem
	{
	    [JsonProperty ("occursAt")]
	    public EventLocation OccursAt {get;set;} 

	    [JsonProperty ("description")]
	    public string Description {get;set;} 

	}

	// EntitySetName: PublicTransportation
	public partial class PublicTransportation : PlanItem
	{
	    [JsonProperty ("seatNumber")]
	    public string SeatNumber {get;set;} 

	}

	// EntitySetName: Flight
	public partial class Flight : PublicTransportation
	{
	    [JsonProperty ("flightNumber")]
	    public string FlightNumber {get;set;} 

	}

	// EntitySetName: Employee
	public partial class Employee : Person
	{
	    [JsonProperty ("cost")]
	    public long Cost {get;set;} // not null

	}

	// EntitySetName: Manager
	public partial class Manager : Person
	{
	    [JsonProperty ("budget")]
	    public long Budget {get;set;} // not null

	    [JsonProperty ("bossOffice")]
	    public Location BossOffice {get;set;} 

	}

	public enum PersonGender
	 {
 		Male=0,
		Female=1,
		Unknown=2 
	}
	public enum Feature
	 {
 		Feature1=0,
		Feature2=1,
		Feature3=2,
		Feature4=3 
	}
}
------------

Password is encrypted during runtime and is no more stored as plain text

For security, the password is encrypted during runtime and is not stored as plain text.

o2pgen reads the password and token from either the command line or a file, encrypts them, and stores them in a SecureContainer class. Once the password and token are no longer needed, o2pgen clears them from memory and overwrites their contents with zeros to ensure that no trace of the password or token remains.

When a password is entered via the command line, o2pgen stores it as a SecureString and encrypts it. The password is decrypted at the point of use, but it is never stored as plain text. This ensures that the password remains secure even if the system is compromised.


Reading password from keyboard and Encrypted

To enter the password for o2pgen, use a single dash - or question mark ? as a placeholder. When prompted for the password, o2pgen will read characters from the console and mask them with asterisks for security.

example

use ? as placeHolder

-u user1 -p ?

OR use single dash - as PlaceHolder

-u user1 -p -

After displaying the message Enter password: on the screen, o2pgen waits for the user to enter the password using the keyboard, one character at a time. For security, the password is displayed on the screen as *****.

Once the password is entered, o2pgen stores the encrypted password in a secured container. When the password is needed, o2pgen decrypts it and then overwrites the contents of the password with zeros to ensure that it cannot be accessed from memory.


Load option and arguments of commandLine from a text configuration File

A. How the configuration File Work

You can automate the execution of o2pgen by specifying a configuration file when you start O2pgen. The configuration file is a text file with extension ".txt" or any extension you want. O2pgen uses the values contained in the configuration file to provide the options of the arguments of the commandLine.

B- Running o2pgen Using a configuration File

Now, you are ready to run at the command line, specifying the configuration file you created. For help information about the full set of these options, run the command with the help option, for example:

> o2pgen --help

To run o2pgen using a configuration file named myconfig.txt, prefix the file name with @:

REM Don't include extra parametrs when you call the configuration file.

O2pgen @path/to/myconfig.txt

the File may be relative or absolute one.

If you specify a relative path to the configuration file, it will be relative to the current working directory.

The following rules are applied to the configuration file:

  • Every line may contain one or more option. The Comment lines are started with # and it will be ignored. The empty lines will be ignored.
  • Option values can be Environment variables based on the standard of the operating system. On windows, it is contained between % %, e.g %TOKEN% and case insensetive. In Linux/Mack Os it prefixed by $, e.g $TOKEN. It read based on OS.
  • If the option value is ? for password, o2pgen read value from keyboard. e.g -p ?
  • If the option value is preceded by @@. The value will be read from file on disk. For example a token is stored in file named path/to/token.txt. This help in reading tokens stored in files. Also you can include external file by @@fileName which may be shared betwwen different configuration files.
  • The sequence options (multi values), can be written in multi lines, like:
# attributes -a can be one line:  key,req,json or be written in multilines
-a key
 req
 json
  • Configuration file can include other configuration file using the command #include myconfig2.txt

You can create different configuration files per service and use the required one as needed.

c- The default configuration file

If you run o2pgen without any parameter and the current directory include file named o2pgen.txt, it will be used as a configuration file.

Example1:

The next configuration file config1.txt, contains environment variables in windows with comments and empty lines.

#-------Odata Service 
#for development test
#-r https://localhost/odata

# for production
-r https://services.odata.org/V4/OData/OData.svc
--auth oauth2 
 # environment var: client_id
-u %client_id%     
-p %client_secret% 
--token-endpoint https://MtTokenServer.us.com/oauth/token
--token-params  "audience=https://www.todo.com"
-v

To use:

o2pgen @config1.txt

Example 2:

The file config2.txt, read password from keyboard.

-r https://services.odata.org/V4/OData/OData.svc
--auth basic 
-u user1
# read password from keyboard
-p ?

To use:

o2pgen @config2.txt

Example 3:

The file config3.txt, read token from file.

-r https://services.odata.org/V4/OData/OData.svc
--auth token 
# read password from keyboard
-p @@path/to/mytoken.txt

Example 4:

The file config3.txt, read token from file.

-r https://services.odata.org/V4/OData/OData.svc
--auth none
# new feature, to pass header to httpClient
# authentication read token from environment var
--http-header "Authorization=Bearer %token%"
    key1=123
    key2=abc
    Accept=application/json

Exampl 5:

This example show how to include file that has common options in the configuration file.

The file header.txt has a shared options used with other configuration files

a) Create a text file named header.txt with the contents:

# file: header.txt

 -H "Authorization=Bearer %token%"
 -H  key1=123
 -H  key2=abc
 -H  Accept=application/json

b) Create the main configuration file named config5.txt that refer to the previous file header.txt

#File: config5.txt
#--url http://localhost/odata
-r https://services.odata.org/TripPinRESTierService

# attribute list in multi lines
-a key
  req
  json

#include the shared file header.txt (relative or absolute path)
#include header.txt

To use

o2pgen @config5.txt

Now config5.txt contain all the parameters of both files.


Reading value of any option in the commandLine from file

If you use tte --auth token to connect with oData service, you can pass the file containing the token using a prefix @@ like @@path/to/token.txt.

Example

-r http://myservice.com -auth token -p @@tripToken.txt

The above exampl use the file tripToken.txt which contain the token.


New Option: Allow setting of SSL-TLS protocols

The option `--ssl', enable the specified TLS protocol when making HTTPS requests.

Allowed Valid values are: Tls, Tls11, Tls12, Tls13. By default, all available protocols are enabled. Note that enabling older, less secure protocols like TLS 1.0 or TLS 1.1 may pose a security risk. Consult with your system administrator or IT department if you're not sure which protocol to use.

Example

Enables TLS 1.2 for HTTPS requests.

o2pgen.exe --ssl tls12

Example

Enables TLS 1.1 and TLS 1.2 for HTTPS requests.

o2pgen.exe --ssl tls11,tls12

Note:

Users may need to run the command prompt or terminal window with elevated privileges (i.e., "Run as administrator") in order to use the --ssl option, depending on the security settings of their system.

When this option is useful

Enabling specific TLS protocols can be useful in certain situations, such as:

  1. Encountering compatibility issues: Some servers or clients may be configured to use older TLS protocols that are not compatible with newer protocols. In this case, you may need to enable an older TLS protocol, such as TLS 1.0 or TLS 1.1, in order to establish a connection.

  2. Compliance with security policies: Some organizations may have strict security policies that require the use of specific TLS protocols. In this case, you may need to enable a specific TLS protocol in order to comply with these policies.

  3. Performance optimization: In some cases, enabling specific TLS protocols can improve the performance of your application by reducing the overhead of the TLS handshake. For example, TLS 1.3 introduces a number of performance optimizations that can reduce the latency and improve the throughput of HTTPS requests.

Note that enabling older, less secure TLS protocols like TLS 1.0 or TLS 1.1 should be done with caution, as it can make your application vulnerable to security threats. You should only enable these protocols if you have a specific need to do so, and you should consider upgrading your server or client software to support newer, more secure TLS protocols.


New option: Allow to Skip Certification Check in http connection

The help text:

-S, --skip-check                   Skips certificate validation checks that include all validations such as trusted root authority, expiration, ... .

Ignore SSL certificate errors when making HTTPS requests. This option can be useful for testing or when connecting to a server with a self-signed certificate. Use this option only when you're sure that you trust the server you're connecting to, and you understand the implications of ignoring SSL certificate errors. When this option is useful:

  • This option can be useful for testing or when connecting to a server with a self-signed certificate.

  • This option should not be used in production environments (except you trust the server)

  • If you're not sure whether you should use this option, consult with your system administrator or IT department.


New Option: Allow to specify Http header in Odata http connection with the computing of base64

The New option: --http-header or -H enable to pass http header to httpClient. Http Header as a list of key/value pair separated by ';' e.g. key1:value1;ky2:value2.

Example

This example show how to pass Bearer token for Authorization and extra headers. (Note that auth is none).

o2pgen --url http://localhost/odata --auth none ^
--http-header "Authorization=Bearer %token%";key1=123;key2=abc

Also, header can be entered using the repeating option like:

On Windows:

 -H "Authorization=Bearer %token%" ^
 -H key1=123  ^
 -H key2=abc

On Linux/Mac:

 -H "Authorization=Bearer %token%" \
 -H key1=123  \
 -H key2=abc

Add comments to the header of class to mark the openType classes.

Comments are addeded to the header of the clas to show it origin as Complex Entity or EntitySet

Click to show generated code with comments
namespace Lab01Sample01.Models
{
	// EntitySetName: books
	public partial class book
	{
	    public string id {get;set;} //PrimaryKey not null

	    public string isbn {get;set;} 

	    public string title {get;set;} 

	    public int year {get;set;} 

	    public bool forKids {get;set;} 

	}

	// EntitySetName: authors
	public partial class author
	{
	    public string id {get;set;} //PrimaryKey not null

	    public string authorName {get;set;} 

	    public List<address> addresses {get;set;} 

	}

	// EntitySetName: publishers
	public partial class publisher
	{
	    public string id {get;set;} //PrimaryKey not null

	    public string publisherName {get;set;} 

	    public address address {get;set;} 

	}

	// Complex Entity
	public partial class address
	{
	    public string town {get;set;} 

	    public string county {get;set;} 

	}

	// EntitySetName: Translator
	public partial class Translator
	{
	    public string translatorId {get;set;} //PrimaryKey not null

	    public string translatorName {get;set;} 

	}

}


Support repeating options in the commandLine for sequence args

The sequence option that can be entered with comma or semiColon separator can be repeated

For Example, the sequence for -H option is entered with semi Colon separator like:

-H "Authorization=Bearer %token%";key1=123;key2=abc

The same option Can be repeated like:

On Windows:


 -H "Authorization=Bearer %token%" ^
 -H key1=123  ^
 -H key2=abc

On Linux/Mac:


 -H "Authorization=Bearer %token%" \
 -H key1=123  \
 -H key2=abc

Clone this wiki locally