The VersionOne APIClient is a C# library that allows object-oriented
access to the VersionOne Data API, which is inherently a REST-based
XML web service. Through the APIClient, you can query for simple or
complex sets of information, update the information, and execute
system-defined operations, without having to construct HTTP requests
and responses or deal with XML parsing.
Simple queries can request a single asset with several attributes.
Complex queries can request multiple assets meeting a certain
criteria, have the results sorted in a particular way, and even ask
for a portion (a "page") of the overall results.
The VersionOne APIClient requires the .Net Framework 1.1 or higher
and VersionOne 7.1 or higher. To access the library, make a
reference to the APIClient.dll in your .Net project. The APIClient
can use either VersionOne or Windows Integrated Authentication.
The VersionOne Information Model
Practically all data in VersionOne is stored in the form of assets,
which have attributes. Each asset is classified by an asset type,
which describes a number of attribute definitions, operations,
rules, and possibly an inheritance from another asset type. A list
of all the types within VersionOne can be obtained by accessing the
meta data url of your VersionOne instance. Additionally, VersionOne
comes with an xsl stylesheet, which can be referenced as a parameter
to the meta data url and makes it easier to read the response:
http://localhost/VersionOne/meta.v1/?xsl=api.xsl
Individual types can also be viewed through the meta url:
http://localhost/VersionOne/meta.v1/Story?xsl=api.xsl
You must use the system name for the type you would like to
retrieve. This is true whether using the API directly or the
APIClient. For instance, in the example above the system name is
"Story", which certain methodology templates display as "Backlog
Item" or "Requirement". Here is a list of some of the most important
system names and their corresponding default display names in the
available methodology templates:
Table 1.
|
System Name | XP Display Name | Scrum Display Name |
AgileUP Display Name | DSDM Display Name |
|---|
|
Scope | Project | Project | Project |
Project |
| Timebox | Iteration |
Sprint | Iteration | Iteration |
|
Theme | Theme | Feature Group | Use
Case | Feature Group |
| Story |
Story | Backlog Item | Requirement |
Requirement |
| Defect | Defect |
Defect | Defect | Defect |
|
Task | Task | Task | Task | Task |
|
Test | Test | Test | Test | Test |
Asset Type.
Asset types describe the "classes" of business data available.
Asset types form an inheritance hierarchy, such that each asset type
inherits attribute definitions, operations, and rules from it's "parent"
asset type. Those asset types at the leaves of this hierarchy are
concrete, whereas asset types with "children" asset types
are abstract. Assets are all
instances of concrete asset types. Asset types are identified by unique
names.
By way of example, Story and Defect are concrete asset types. On the
other hand, Workitem is an abstract asset type, from which Story and
Defect ultimately derive.
Attribute Definition.
Attribute definitions describe the properties that "make up"
each asset type. An attribute definition defines the type of its value,
whether it is required and/or read-only, and many other qualities.
Attribute definitions are identified by a name that is unique within its
asset type.
Attribute definitions are defined as either scalars or relations to
other assets. Further, relation attribute definitions can be either
single-value or multi-value. For example, the Estimate attribute
definition on the Workitem asset type is a scalar (specifically, a
floating-point number). On the other hand, the Workitem asset type's
Scope attribute definition is a single-value relation (to a Scope
asset). The reverse relation, Workitems on the Scope asset type, is
a multi-value relation (to Workitem assets).
Asset.
Actual business objects in VersionOne are assets, which are
instances of concrete asset types. Each asset is uniquely identified by
it's asset type and ID (an integer). For example,
Member:20 identifies the Member asset with ID of 20.
Attribute.
On every asset are a number of attributes, which attach specific
values to the attribute definitions defined in the asset type. If the
attribute's definition is a relation, then the value(s) of the attribute
are references to an asset(s).
Moment.
As data changes in VersionOne, a history is maintained. Every
change to every asset is journaled within the system, and assigned a
chronologically-increasing integer called a
moment. A past version of an asset is uniquely identified by
it's asset type, ID, and Moment. A past version of a relation attribute
will refer to the past version of it's target asset. For example,
Member:20:563 identifies the Member asset with ID of 20, as
it was at the time of moment 563.
Learn By Example: APIClient Setup
Using the APIClient is as simple as making a reference to the
APIClient.dll in your .Net project, then providing connection
information to the main service objects within the APIClient. There
are three possible ways to connect to your VersionOne instance using
the APIClient. Before you attempt to connect, find out whether your
VersionOne instance uses VersionOne authentication or Windows
Integrated Authentication. You need to create an instance of
IMetaModel and and instance of IServices and provide them with
connection information via instances of the V1APIConnector. The
first example uses VersionOne authentication:
V1APIConnector dataConnector = new V1APIConnector("http://server/v1instance/rest-1.v1/", "username", "password");
V1APIConnector metaConnector = new V1APIConnector("http://server/v1instance/meta.v1/");
IMetaModel metaModel = new MetaModel(metaConnector);
IServices services = new Services(metaModel, dataConnector);
If your VersionOne instance uses Windows Integrated Authentication,
and you wish to connect to the API using the credentials of the user
running your program, you can omit the username and password
arguments to the V1APIConnector:
V1APIConnector dataConnector = new V1APIConnector("http://server/v1instance/rest-1.v1/");
V1APIConnector metaConnector = new V1APIConnector("http://server/v1instance/meta.v1/");
IMetaModel metaModel = new MetaModel(metaConnector);
IServices services = new Services(metaModel, dataConnector);
You may also explicitly identify the domain user you wish to use to
authenticate to VersionOne, and provide an extra boolean argument
indicating that you wish to use Windows Integrated Authentication:
V1APIConnector dataConnector = new V1APIConnector("http://server/v1instance/rest-1.v1/", @"domain\username", "password", true);
V1APIConnector metaConnector = new V1APIConnector("http://server/v1instance/meta.v1/");
IMetaModel metaModel = new MetaModel(metaConnector);
IServices services = new Services(metaModel, dataConnector);
Learn By Example: Queries
This section is a series of examples, starting with simpler queries
and moving to more advanced queries. You'll need to create an
instance of both IMetaModel and IServices, as outlined above, to
perform the queries.
How to query a single asset
Retrieve the
Member with ID 20:
public Asset SingleAsset()
{
Oid memberId = Oid.FromToken("Member:20", metaModel);
Query query = new Query(memberId);
QueryResult result = services.Retrieve(query);
Asset member = result.Assets[0];
Console.WriteLine(member.Oid.Token);
/***** OUTPUT *****
Member:20
******************/
return member;
}
In this example, the asset will have its Oid populated, but
will not have any other attributes populated. This is to
minimize the size of the data sets returned. The next
example shows how to ask for an asset with specific
attributes populated.
How to query for specific attributes
Retrieve an asset with populated attributes by using the Selection
property of the Query object.
public Asset SingleAssetWithAttributes()
{
Oid memberId = Oid.FromToken("Member:20", metaModel);
Query query = new Query(memberId);
IAttributeDefinition nameAttribute = metaModel.GetAttributeDefinition("Member.Name");
IAttributeDefinition emailAttribute = metaModel.GetAttributeDefinition("Member.Email");
query.Selection.Add(nameAttribute);
query.Selection.Add(emailAttribute);
QueryResult result = services.Retrieve(query);
Asset member = result.Assets[0];
Console.WriteLine(member.Oid.Token);
Console.WriteLine(member.GetAttribute(nameAttribute).Value);
Console.WriteLine(member.GetAttribute(emailAttribute).Value);
/***** OUTPUT *****
Member:20
Administrator
admin@company.com
******************/
return member;
}
How to get a list of all Story assets
public AssetList ListOfAssets()
{
IAssetType storyType = metaModel.GetAssetType("Story");
Query query = new Query(storyType);
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
IAttributeDefinition estimateAttribute = storyType.GetAttributeDefinition("Estimate");
query.Selection.Add(nameAttribute);
query.Selection.Add(estimateAttribute);
QueryResult result = services.Retrieve(query);
foreach (Asset story in result.Assets)
{
Console.WriteLine(story.Oid.Token);
Console.WriteLine(story.GetAttribute(nameAttribute).Value);
Console.WriteLine(story.GetAttribute(estimateAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Story:1083
View Daily Call Count
5
Story:1554
Multi-View Customer Calendar
1 ...
******************/
return result.Assets;
}Depending on your security
role, you may not be able to see all the Story assets in the
entire system.
Use the Filter
property of the Query object to filter the results that are
returned. This query will retrieve only Story assets with a To Do of
zero:
public AssetList FilterListOfAssets()
{
IAssetType taskType = metaModel.GetAssetType("Task");
Query query = new Query(taskType);
IAttributeDefinition nameAttribute = taskType.GetAttributeDefinition("Name");
IAttributeDefinition todoAttribute = taskType.GetAttributeDefinition("ToDo");
query.Selection.Add(nameAttribute);
query.Selection.Add(todoAttribute);
query.Filter.Include(todoAttribute, 0);
QueryResult result = services.Retrieve(query);
foreach (Asset task in result.Assets)
{
Console.WriteLine(task.Oid.Token);
Console.WriteLine(task.GetAttribute(nameAttribute).Value);
Console.WriteLine(task.GetAttribute(todoAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Task:1153
Code Review
0
Task:1154
Design Component
0 ...
******************/
return result.Assets;
}Use the OrderBy
property of the Query object to sort the results. This query will
retrieve Story assets sorted by increasing Estimate:
public AssetList SortListOfAssets()
{
IAssetType storyType = metaModel.GetAssetType("Story");
Query query = new Query(storyType);
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
IAttributeDefinition estimateAttribute = storyType.GetAttributeDefinition("Estimate");
query.Selection.Add(nameAttribute);
query.Selection.Add(estimateAttribute);
query.OrderBy.MinorSort(estimateAttribute, OrderBy.Order.Ascending);
QueryResult result = services.Retrieve(query);
foreach (Asset story in result.Assets)
{
Console.WriteLine(story.Oid.Token);
Console.WriteLine(story.GetAttribute(nameAttribute).Value);
Console.WriteLine(story.GetAttribute(estimateAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Story:1073
Add Order Line
1
Story:1068
Update Member
2 ...
******************/
return result.Assets;
}
There are two methods you can call on the OrderBy class to sort
your results: MinorSort and MajorSort. If you are sorting by
only one field, it does not matter which one you use. If you
want to sort by multiple fields, you need to call either
MinorSort or MajorSort multiple times. The difference is: Each
time you call MinorSort, the parameter will be added to the end
of the OrderBy statement. Each time you call MajorSort, the
parameter will be inserted at the beginning of the OrderBy
statement.
How to select a portion of query results
Retrieve a "page" of query results by using the Paging propery of
the Query object. This query will retrieve the first 3 Story assets:
public AssetList PageListOfAssets()
{
IAssetType storyType = metaModel.GetAssetType("Story");
Query query = new Query(storyType);
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
IAttributeDefinition estimateAttribute = storyType.GetAttributeDefinition("Estimate");
query.Selection.Add(nameAttribute);
query.Selection.Add(estimateAttribute);
query.Paging.PageSize = 3;
query.Paging.Start = 0;
QueryResult result = services.Retrieve(query);
foreach (Asset story in result.Assets)
{
Console.WriteLine(story.Oid.Token);
Console.WriteLine(story.GetAttribute(nameAttribute).Value);
Console.WriteLine(story.GetAttribute(estimateAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Story:1063
Logon
2
Story:1064
Add Customer Details
2
Story:1065
Add Customer Header
3
******************/
return result.Assets;
}The PageSize property shown
asks for 3 items, and the Start property indicates to start at
0. The next 3 items can be retrieve with PageSize=3, Start=3.
How to query the history of a single asset
This query will retrieve the history of the Member asset with ID
1000.
public AssetList HistorySingleAsset()
{
IAssetType memberType = metaModel.GetAssetType("Member");
Query query = new Query(memberType, true);
IAttributeDefinition idAttribute = memberType.GetAttributeDefinition("ID");
IAttributeDefinition changeDateAttribute = memberType.GetAttributeDefinition("ChangeDate");
IAttributeDefinition emailAttribute = memberType.GetAttributeDefinition("Email");
query.Selection.Add(changeDateAttribute);
query.Selection.Add(emailAttribute);
query.Filter.Include(idAttribute, "Member:1000");
QueryResult result = services.Retrieve(query);
AssetList memberHistory = result.Assets;
foreach (Asset member in memberHistory)
{
Console.WriteLine(member.Oid.Token);
Console.WriteLine(member.GetAttribute(changeDateAttribute).Value);
Console.WriteLine(member.GetAttribute(emailAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Member:1000:105
4/2/2007 1:22:03 PM
andre.agile@company.com
Member:1000:101
3/29/2007 4:10:29 PM
andre@company.net
******************/
return memberHistory;
}
To create a history query, provide a boolean "true" second
argument to the Query constructor.
How to query the history of many assets
This query will retrieve history for all Member assets:
public AssetList HistoryListOfAssets()
{
IAssetType memberType = metaModel.GetAssetType("Member");
Query query = new Query(memberType, true);
IAttributeDefinition changeDateAttribute = memberType.GetAttributeDefinition("ChangeDate");
IAttributeDefinition emailAttribute = memberType.GetAttributeDefinition("Email");
query.Selection.Add(changeDateAttribute);
query.Selection.Add(emailAttribute);
QueryResult result = services.Retrieve(query);
AssetList memberHistory = result.Assets;
foreach (Asset member in memberHistory)
{
Console.WriteLine(member.Oid.Token);
Console.WriteLine(member.GetAttribute(changeDateAttribute).Value);
Console.WriteLine(member.GetAttribute(emailAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Member:1010:106
4/2/2007 3:27:23 PM
tammy.coder@company.com
Member:1000:105
4/2/2007 1:22:03 PM
andre.agile@company.com
Member:1000:101
3/29/2007 4:10:29 PM
andre@company.net
******************/
return memberHistory;
}
Again, the response is a list of historical assets. There
will be multiple Asset objects returned for an asset that
has changed previously.
All of the previously demonstrated query properties can
be used with historical queries also.
How to query an asset "as of" a specific time
Use the AsOf property of the Query object to retrieve data as it
existed at some point in time. This query finds the version of
each Story asset as it existed seven days ago:
public AssetList HistoryAsOfTime()
{
IAssetType storyType = metaModel.GetAssetType("Story");
Query query = new Query(storyType, true);
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
IAttributeDefinition estimateAttribute = storyType.GetAttributeDefinition("Estimate");
query.Selection.Add(nameAttribute);
query.Selection.Add(estimateAttribute);
query.AsOf = DateTime.Now.AddDays(-7); //7 days ago
QueryResult result = services.Retrieve(query);
foreach (Asset story in result.Assets)
{
Console.WriteLine(story.Oid.Token);
Console.WriteLine(story.GetAttribute(nameAttribute).Value);
Console.WriteLine(story.GetAttribute(estimateAttribute).Value);
Console.WriteLine();
}
/***** OUTPUT *****
Story:1063
Logon
3
Story:1064
Add Customer Details
1
Story:1065
Add Customer Header
3
******************/
return result.Assets;
}
Learn By Example: Updates
Updating assets through the APIClient involves calling the Save
method on the IServices object.
How to update a scalar attribute on an asset
Updating a scalar attribute on an asset is accomplished by
calling the SetAttribute method on an asset, specifying the
IAttributeDefinition of the attribute you wish to change and the
new scalar value. This code will update the Name attribute on
the Story with ID 1094:
public Asset UpdateScalarAttribute()
{
Oid storyId = Oid.FromToken("Story:1094", metaModel);
Query query = new Query(storyId);
IAssetType storyType = metaModel.GetAssetType("Story");
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
query.Selection.Add(nameAttribute);
QueryResult result = services.Retrieve(query);
Asset story = result.Assets[0];
string oldName = story.GetAttribute(nameAttribute).Value.ToString();
story.SetAttributeValue(nameAttribute, GetNewName());
services.Save(story);
Console.WriteLine(story.Oid.Token);
Console.WriteLine(oldName);
Console.WriteLine(story.GetAttribute(nameAttribute).Value);
/***** OUTPUT *****
Story:1094:1446
Logon
New Name
******************/
return story;
}
How to update a single-value relation on an asset
Updating a single-value relation is accomplished by calling the
SetAttribute method on an asset, specifying the
IAttributeDefinition of the attribute you wish to change and the
ID for the new relation. This code will change the source of the
Story with ID 1094:
public Asset UpdateSingleValueRelation()
{
Oid storyId = Oid.FromToken("Story:1094", metaModel);
Query query = new Query(storyId);
IAssetType storyType = metaModel.GetAssetType("Story");
IAttributeDefinition sourceAttribute = storyType.GetAttributeDefinition("Source");
query.Selection.Add(sourceAttribute);
QueryResult result = services.Retrieve(query);
Asset story = result.Assets[0];
string oldSource = story.GetAttribute(sourceAttribute).Value.ToString();
story.SetAttributeValue(sourceAttribute, GetNextSourceID(oldSource));
services.Save(story);
Console.WriteLine(story.Oid.Token);
Console.WriteLine(oldSource);
Console.WriteLine(story.GetAttribute(sourceAttribute).Value);
/***** OUTPUT *****
Story:1094:1446
StorySource:148
StorySource:149
******************/
return story;
}
How to add and remove values from a multi-value relation
Updating a multi-value relation is accomplished by calling
either the RemoveAttributeValue or AddAttributeValue method on
an asset, specifying the IAttributeDefinition of the attribute
you wish to change and the ID of the relation you wish to add or
remove. This code will add one Member and remove another Member
from the Story with ID 1094:
public Asset UpdateMultiValueRelation()
{
Oid storyId = Oid.FromToken("Story:1094", metaModel);
Query query = new Query(storyId);
IAssetType storyType = metaModel.GetAssetType("Story");
IAttributeDefinition ownersAttribute = storyType.GetAttributeDefinition("Owners");
query.Selection.Add(ownersAttribute);
QueryResult result = services.Retrieve(query);
Asset story = result.Assets[0];
ArrayList oldOwners = new ArrayList();
oldOwners.AddRange(story.GetAttribute(ownersAttribute).Values);
story.RemoveAttributeValue(ownersAttribute, GetOwnerToRemove(oldOwners));
story.AddAttributeValue(ownersAttribute, GetOwnerToAdd(oldOwners));
services.Save(story);
Console.WriteLine(story.Oid.Token);
foreach (Oid oid in oldOwners)
{
Console.WriteLine(oid.Token);
}
foreach (Oid oid in story.GetAttribute(ownersAttribute).Values)
{
Console.WriteLine(oid.Token);
}
/***** OUTPUT *****
Story:1094:1446
Member:1003
Member:1000
******************/
return story;
}
Learn By Example: New Asset
When you create a new asset in the APIClient you need to specify the
"context" of another asset that will be the parent. For example, if
you create a new Story asset you can specify which Scope it should
be created in.
How to get a new Story asset template in the context of a Scope
asset
This code will create a Story asset
in the context of Scope with ID 1012:
public Asset AddNewAsset()
{
Oid projectId = Oid.FromToken("Scope:1012", metaModel);
IAssetType storyType = metaModel.GetAssetType("Story");
Asset newStory = services.New(storyType, projectId);
IAttributeDefinition nameAttribute = storyType.GetAttributeDefinition("Name");
newStory.SetAttributeValue(nameAttribute, "My New Story");
services.Save(newStory);
Console.WriteLine(newStory.Oid.Token);
Console.WriteLine(newStory.GetAttribute(storyType.GetAttributeDefinition("Scope")).Value);
Console.WriteLine(newStory.GetAttribute(nameAttribute).Value);
/***** OUTPUT *****
Story:1094
Scope:1012
My New Story
******************/
return newStory;
}
Learn By Example: Operations
An operation is an action that is executed against a single asset.
For example, to delete an asset you must execute the Delete
operation on the asset. To close or inactivate a Workitem, you must
use the Inactivate Operation. Available operations for each asset
are listed at the bottom of the the meta data description for that
asset, for instance:
http://localhost/VersionOne/meta.v1/Story?xsl=api.xsl
How to delete a Story asset
Get the Delete
operation from the IMetaModel, and use IServices to execute it
against a story Oid.
public Oid DeleteAsset()
{
Asset story = AddNewAsset();
IOperation deleteOperation = metaModel.GetOperation("Story.Delete");
Oid deletedID = services.ExecuteOperation(deleteOperation, story.Oid);
try
{
Query query = new Query(deletedID.Momentless);
services.Retrieve(query);
}
catch(WebException)
{
Console.WriteLine("Story has been deleted: " + story.Oid.Momentless);
}
/***** OUTPUT *****
Story has been deleted: Story:1049
******************/
return deletedID;
}
The delete operation returns the Oid, with the new Moment, of
the deleted asset. Future current info queries will
automatically exclude deleted assets from results.
Currently, there is no support for undeleting a deleted
asset.
How to close a Story asset
Get the
Inactivate operation from the IMetaModel, and use IServices to
execute it against a story Oid.
public Asset CloseAsset()
{
Asset story = AddNewAsset();
IOperation closeOperation = metaModel.GetOperation("Story.Inactivate");
Oid closeID = services.ExecuteOperation(closeOperation, story.Oid);
Query query = new Query(closeID.Momentless);
IAttributeDefinition assetState = metaModel.GetAttributeDefinition("Story.AssetState");
query.Selection.Add(assetState);
QueryResult result = services.Retrieve(query);
Asset closeStory = result.Assets[0];
AssetState state = (AssetState) closeStory.GetAttribute(assetState).Value;
Console.WriteLine(closeStory.Oid);
Console.WriteLine(Enum.GetName(typeof(AssetState), state));
/***** OUTPUT *****
Story:1050
Closed
******************/
return closeStory;
}
The AssetState attribute is the internal state of an asset.
How to reopen a Story asset
Get the
Reactivate operation from the IMetaModel, and use IServices to
execute it against a story Oid.
public Asset ReOpenAsset()
{
Asset story = CloseAsset();
IOperation activateOperation = metaModel.GetOperation("Story.Reactivate");
Oid activeID = services.ExecuteOperation(activateOperation, story.Oid);
Query query = new Query(activeID.Momentless);
IAttributeDefinition assetState = metaModel.GetAttributeDefinition("Story.AssetState");
query.Selection.Add(assetState);
QueryResult result = services.Retrieve(query);
Asset activeStory = result.Assets[0];
AssetState state = (AssetState)activeStory.GetAttribute(assetState).Value;
Console.WriteLine(activeStory.Oid);
Console.WriteLine(Enum.GetName(typeof(AssetState), state));
/***** OUTPUT *****
Story:1051
Active
******************/
return activeStory;
}
Copyright © 2007, VersionOne, LLC. All rights reserved. This document
was generated 2007-04-16 11:30:10.