- Create a new library or console project using
dotnet new classlib
ordotnet new console
. - Add a referece to the EmsApi package with
dotnet add package EmsApi.Client
.
- Start a new project or solution in Visual Studio using
.NET Framework 4.6.1+
. - In Solution Explorer, right click the References entry under the project and select Manage Nuget Packages...
- Search for "EmsApi", select and install "EmsApi.Client".
For almost all use cases a username, password, and API endpoint should be specified in order to connect. They may be retrieved from environment variables as well.
using System;
using EmsApi.Client.V2;
namespace TestApp
{
class Program
{
static void Main( string[] args )
{
var config = new EmsApiServiceConfiguration()
{
// If this is not set, it will be retrieved from the "EmsApiEndpoint" environment variable.
// If neither are set, it will default to EmsApiEndpoints.Default.
Endpoint = "https://myapiserver/api",
// If this is not set, it will be retrieved from the "EmsApiUsername" environment variable.
UserName = "my-username",
// If this is not set, it will be retrieved from the "EmsApiPassword" environment variable.
// The environment variable must be a base64 encoded UTF-8 string containing the password.
Password = "p@ssw0rd",
// Optional application name to use, this is passed to the API server to assist in debugging
ApplicationName = "MyCoolApp"
};
using( EmsApiService api = new EmsApiService( config ) )
{
// Your code goes here, call functions on the EmsApiService object...
}
}
}
}
Most method calls take in an optional CallContext argument that includes various per-call configuration.
This is used to enable Trusted Authentication by setting the TrustedAuthValue and (optionally) TrustedAuthName settings, as opposed to using username and password authentication described above.
// This pattern passes the trusted authentication name and value to every method call:
using( EmsApiService api = new EmsApiService( config ) )
{
// Send all requests to the ems system with the id 1.
var ctx = new CallContext
{
TrustedAuthName = "SAMAccountName",
TrustedAuthValue = "bob.burgers"
};
var fleets = api.Assets.GetAllFleets( ctx );
var aircraft = api.Assets.GetAllAircraft( ctx );
}
// This pattern sets the trusted authentication name in the config and passes the value to every method call:
config.TrustedAuthName = "SAMAccountName";
using( EmsApiService api = new EmsApiService( config ) )
{
var ctx = new CallContext
{
TrustedAuthValue = "bob.burgers"
};
var fleets = api.Assets.GetAllFleets( ctx );
var aircraft = api.Assets.GetAllAircraft( ctx );
}
Access to different API routes is exposed through various properties on the EmsApiService class. For instance,
to query the database you would use the api.Databases
property, and call one of the methods on that object.
The EmsApi.Dto.V2 namespace contains objects that are returned from API calls, as well as a few types that are required to make API calls, including the database query routes.
using System;
using EmsApi.Client.V2;
using EmsApi.Dto.V2;
namespace TestApp
{
class Program
{
static void Main( string[] args )
{
using( EmsApiService api = new EmsApiService() )
{
var query = new DatabaseQuery();
// Select some fields in the query.
query.SelectField( Monikers.FlightId );
query.SelectField( Monikers.TailNumber );
query.SelectField( Monikers.CityPair );
query.SelectField( Monikers.TakeoffAirportName );
query.SelectField( Monikers.LandingAirportName );
// Filter for takeoff and landing valid.
query.AddBooleanFilter( Monikers.TakeoffValid, true );
query.AddBooleanFilter( Monikers.LandingValid, true );
// Order by flight id.
query.OrderByField( Monikers.FlightId );
// Limit to first 100 flights.
query.Top = 100;
// Execute the query and receive the result.
DatabaseQueryResult result = api.Databases.Query( Monikers.FlightDatabase, query );
foreach( DatabaseQueryResult.Row row in result.Rows )
{
// Go through each result row and print out the information.
int flightId = Convert.ToInt32( row[Monikers.FlightId] );
string tail = row[Monikers.TailNumber].ToString();
string cityPair = row[Monikers.CityPair].ToString();
string takeoffAirport = row[Monikers.TakeoffAirportName].ToString();
string landingAirport = row[Monikers.LandingAirportName].ToString();
string msg = string.Format( "Flight {0} with tail number {1} from {2} to {3} ({4}).",
flightId, tail, takeoffAirport, landingAirport, cityPair );
Console.WriteLine( msg );
}
}
}
private static class Monikers
{
public static string FlightDatabase = "[ems-core][entity-type][foqa-flights]";
public static string FlightId = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[ems-core][base-field][flight.uid]]]";
public static string TailNumber = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[ems-core][base-field][flight.aircraft]]]";
public static string CityPair = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[ems-core][base-field][city-pair.pair]]]";
public static string TakeoffAirportName = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[[nav][type-link][airport-takeoff * foqa-flights]]][[nav][base-field][nav-airport.name]]]";
public static string LandingAirportName = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[[nav][type-link][airport-landing * foqa-flights]]][[nav][base-field][nav-airport.name]]]";
public static string TakeoffValid = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[ems-core][base-field][flight.exist-takeoff]]]";
public static string LandingValid = "[-hub-][field][[[ems-core][entity-type][foqa-flights]][[ems-core][base-field][flight.exist-landing]]]";
}
}
}
This query works similarly to the database query. You construct a query object, and then add some parameter ids to include, and then execute it for a specific flight.
using System;
using System.Linq;
using EmsApi.Client.V2;
using EmsApi.Dto.V2;
namespace TestApp
{
class Program
{
static void Main( string[] args )
{
using( EmsApiService api = new EmsApiService() )
{
var query = new AnalyticQuery();
// Include radio altitude id in the time-series query.
query.SelectAnalytic( RadioAltitudeId );
// Execute the query.
QueryResult result = api.Analytics.QueryResults( flightId: 1, query: query );
// Access the results.
double[] offsets = result.Offsets.ToArray();
// Note: The Results array contains one object per parameter in the query, but we only
// added one parameter so we take the first one here.
object[] values = result.Results.First().Values.ToArray();
for( int i = 0; i < offsets.Length; ++i )
{
int offset = Convert.ToInt32( offsets[i] );
double value = Convert.ToDouble( values[i] );
Console.WriteLine( "{0}: {1}", offset, value );
}
}
}
private const string RadioAltitudeId = "H4sIAAAAAAAEAG2QQQuCQBCF74H/Qby7qyUUokJQB8EuidB1WzcdWFfbXbOf35ZYSb3DY2DmY95MdGSq5Tdy5iwtmdBwASbte8OFCiF2aq27EONhGNCwQq2s8NLzfHw6ZDmtWUNcEEoTQZkzMm9CvdoKlUQT2gotCdUTH2BvjbcloEISJ7EWth2NKZhMy2QqFdo3KmsroIRH+GtgBuQdoyYz3Zk9NoQCeOxo2Zs8+P9gIeDam1sTb5TvGtu4gfFnNdqkz94f3FpMzfnvkgfLSh/6UgEAAA==";
}
}
The library supports using environment variables for configuration instead of specifying the configuration in your application.
Variable Name | Description |
---|---|
EmsApiEndpoint | The API url to communicate with. This url must include the /api portion, such as "https://localhost/api". |
EmsApiUsername | The username to use for authentication with the api. |
EmsApiPassword | The base64 encoded password for the user. |
EmsApiClientId | The client id to use for trusted authentication with the api. |
EmsApiClientSecret | The base64 encoded client secret for the client id. |
EmsApiTrustedAuthName | The name of the property to use when locating a trusted user at token creation time. This can be provided in the CallContext as well. |
- .NET 5
- Visual Studio 2019 >= 16.8 (Optional)
- Text editor or IDE of your choice
- An EditorConfig extension to enforce code style rules
- The SpecFlow extension for writing unit tests
The project is built as a .netstandard2.0 library which will allow it to work with .NET Framework 4.6.1+ and .NET core 2.0+. Supported frameworks can be found here. The unit test project targets .NET 5.
Run dotnet build
in the src
directory or build with Visual Studio.
Run dotnet test
in the src
directory or use the Visual Studio test explorer. Many tests will talk to a real API endpoint and require the EmsApiTestEndpoint
, EmsApiTestUsername
, EmsApiTestPassword
, EmsApiTestClientId
, and EmsApiTestClientSecret
environment variables to be set.
The project is built using an Azure DevOps pipeline. Pull requests to the master
branch are built and the nuget packages are attached to the pipeline run. The master
branch is also built once the pull request is completed.
- Prior to a release, as part of a pull request:
- The version number should be updated in
src/Directory.Build.props
- The release notes file in the root should be updated
- The version number should be updated in
- Once the pull request completes and the build pipeline for the
master
branch succeeds, run the release pipeline. Using the latest master branch build this will:- Create a new GitHub release and tag the commit with
vX.X.X
, where the version number matches the contents of the Build.Directory.props file - Push the nupkg files to NuGet.org
- Create a new GitHub release and tag the commit with
Note that the latest commit to master
will be tagged with the release, so a release should not be created unless the latest commit to master
has succeeded the build pipeline.