I am working with an SDK that can query a set of data as well as update data with a restful web service called VersionOne. We use the web service to document QA testing. Each test has attributes such as "Name", "Status", etc. Most of the attributes have been successfully updating except for "Status".
Here is the method I am calling, when I step through the code I can get the old value but cannot change the attribute value as expected. An error stating "Cannot assign new value to a read-only attribute".
public bool TestInProgress()
{
var testId = Oid.FromToken("Test:26017", _context.MetaModel);
var query = new Query(testId);
var assetType = _context.MetaModel.GetAssetType("Test");
var testStatus = assetType.GetAttributeDefinition("Status.Name");
query.Selection.Add(testStatus);
var result = _context.Services.Retrieve(query);
var test = result.Assets[0];
var oldResult = GetValue(test.GetAttribute(testStatus).Value);
test.SetAttributeValue(testStatus, "Failed");
_context.Services.Save(test);
LogResult(test.Oid.Token, oldResult, GetValue(test.GetAttribute(testStatus).Value));
Console.WriteLine(test.Oid.Token, oldResult, GetValue(test.GetAttribute(testStatus).Value));
return true;
}
https://github.com/versionone/VersionOne.SDK.Net.APIClient#attribute-definition
According to the VersionOne SDK documentation it appears as though "read-only" and is an attribute. I've looked though the different attribute from several different tests and testsets and do not see it. I am authenticated properly and have successfully updated other attributes with many different tests. However, when I attempt to programmatically change the "Status" attribute it says it is read-only.
https://github.com/versionone/VersionOne.SDK.Net.APIClient#learn-by-example-apiclient-setup
How do you change the attribute for an asset in VersionOne programmatically that is currently read-only so you can update the attribute using the restful web service?
Because the Attribute is read-only, you will not be able to change its value. Instead, consider creating a 'new' Asset, set its Attributes, and then save it.
Review the example below and attempt to utilize the idea within your project:
var TestId = Oid.FromToken("Test:26017", _context.MetaModel);
var TestAsset = _context.MetaModel.GetAssetType("Test");
var newTestAsset = _context.Services.New(TestAsset, TestId);
var TestStatusAttr = newTestAsset.GetAttributeDefinition("Status.Name");
newTestAsset.SetAttributeValue(TestStatusAttr, "Failed");
_context.Services.Save(newTestAsset);
Related
When programmatically creating a Cognito user pool and app client, if the app client is to have read/write access to attributes of the user pool, that access must be explicitly given. I have been able to do so successfully for custom attributes but built-in attributes always return an error of "Invalid write attributes specified while creating a client" or "Invalid read attributes specified while creating a client".
Documentation is ... both voluminous and difficult to find. I have yet to see an example of this or an actual bit of useful documentation on the CreateUserPoolClientRequest type that says anything about this other than things like "ReadAttributes is a list of strings that are the attributes that can be read".
Here is the code I'm using that always ends up with that error message and failure to create the app client. _client is an AmazonCognitoIdentityProviderClient properly instantiated and credentialed and running in a lambda function.
var request = new CreateUserPoolClientRequest { UserPoolId = userPoolId, ClientName = $"{name}AppClient" };
var builtInAttributes = new List<string>()
{
"address","birthdate","email","family name","gender","given name","locale","middle name","name","nickname","phone number", "picture","preferred username","profile","zoneinfo","updated at","website"
};
var readAttributes = new List<string>();
var writeAttributes = new List<string>();
readAttributes.InsertRange(0,builtInAttributes);
writeAttributes.InsertRange(0, builtInAttributes);
var attributeConfig = ConfigurationHelper.GetListFromSection("UserPoolCustomAttributes");
foreach (var attribute in attributeConfig)
{
readAttributes.Add($"custom:{attribute.Key}");
writeAttributes.Add($"custom:{attribute.Key}");
}
request.ReadAttributes = readAttributes;
request.WriteAttributes = writeAttributes;
var result = await _client.CreateUserPoolClientAsync(request, CancellationToken.None);
Any help is greatly appreciated.
Figured it out. Though I have yet to find it documented anywhere, default attributes with a space in the name in the ui need to have that space replaced with an underscore when using their name in the api.
So, I'm writing an app to 'flesh out' new clients in Rally. It will have tools to create templates which will add first:
add a 'Feature'
add 'UserStories' under that 'Feature'
add 'Tasks' under those 'UserStories' individually
I have figured out step 1. But how to associate anything I can't figure out from the horrible and cryptic documentation. Here's what I have so far:
var FeatureToAdd = _featureRepository.GetFeatures().FirstOrDefault(x => x.Id == 2);
// Initialize the REST API. You can specify a web service version if needed in the constructor.
RallyRestApi restApi = GetRallyRestApi();
//Create an item
DynamicJsonObject toCreate = new DynamicJsonObject();
toCreate["Name"] = FeatureToAdd.Name;
toCreate["Description"] = FeatureToAdd.Description;
// important to which this belongs, but I no ID I ever use works
//toCreate["Workspace"] = "/workspace/" + WebConfigurationManager.AppSettings["RallyAPIWorkspaceID"];
//toCreate["Project"] = "/project/XXXXX";
//toCreate["Iteration"] = "/iteration/XXXXXX";
// create feature - feature is under PortfolioItem
CreateResult createFeatureResult = restApi.Create("PortfolioItem/Feature", toCreate);
// scrape ID off the end of the reference
var pureId = createFeatureResult.Reference.Substring(createFeatureResult.Reference.LastIndexOf('/') + 1);
// add UserStories
foreach (UserStory u in FeatureToAdd.UserStories)
{
toCreate = new DynamicJsonObject();
toCreate["Name"] =u.Name;
toCreate["Description"] = u.Description;
toCreate["WorkProduct"] = "PortfolioItem/Feature/" + pureId;
//toCreate["WorkProduct"] = createFeatureResult.Reference;<- tried this too
// hierarchicalrequirement = UserStory
CreateResult createUserStoryResult = restApi.Create("hierarchicalrequirement", toCreate);
}
Running this creates both, but no association happens. I get a warning:
Ignored JSON element hierarchicalrequirement.WorkProduct during processing of this request.
Why did it arbitrarily ignore this?...
It ignored WorkProduct because WorkProduct is not a valid field on HierarchicalRequirement. The field you want to specify to set the feature parent of a story is called PortfolioItem.
toCreate["PortfolioItem"] = Ref.GetRelativeRef(createFeatureResult.Reference);
Also, object relationships are specified as in WSAPI as refs (/type/id) so you can just directly pass in the reference from the createFeatureResult.
Sorry you're finding the api to be frustrating. It definitely has some weird dark corners but once you use it a bit and get a feel for how the various domain objects are related I think you'll find it to be quite powerful and consistent.
I am building a C#/.NET 4.5 client for a REST API using JSON.NET. The API supports partial updates; therefore the presence or lack of an attribute in the json on an update has meaning. If the attribute is in the json, the server will set the value accordingly; the the attribute is not passed the server will not update it. This also applies to null values. I have .NET classes for each model; with properties for each JSON attribute (pretty standard).
As an example lets say I have this account object (name, notes) that already exists on the server:
{
'name':'craig',
'notes:'these are notes'
}
If I pass in this json for an update it will update the name, but will leave the notes set to 'these are notes':
var account = api.GetAccount();
account.Name = "bob";
api.UpdateAccount(account);
{
'name':'bob'
}
If I pass this json in for an update, it will set the name and the notes to null on the server:
var account = api.GetAccount();
account.Name = "bob";
account.Notes = null;
api.UpdateAccount(account);
{
'name':'bob',
'notes':null
}
All good up to this point.
My question is how to you get JSON.NET to play along nicely with this. JSON.NET allows control the NullValueHandling which basically says if null values should be serialized or not. However that is not enough in this case. I need to be able to determine if the calling code explicitly set the value to null. Is there a recommended way to handle this?
Ive tried using a Dictionary internal to my models to store the attributes to be serialized via JSON. This allows me to tell if the attribute has been set to anything (including null) via the presence of the key in the dictionary. I found that this approach has some difficulties and I end up rewriting a lot of code that comes standard to JSON.NET (type serialization, generics, nullables, enums...).
Note: I do realize the above example is a bit contrived. In reality the account object returned back from the server would have both name and notes populated, and that when the update happened it would send both back.
The other case where this applies is during creating objects and handling server generated default. For example, lets say the server defaults the account's notes to 'put notes here' when the account is created. If I pass in the Notes attribute with a null value, the server will think the client wants to set it to null. The reality though is the client is not trying to set the Notes to null, and in this case would want the default to be set.
var account = new Account();
account.Name = "bob";
api.CreateAccount(account);
{
'name':'bob',
'notes':null
}
Im always impressed by JSON.NET...
Here is what I ended up with. I used a combination of a ContractResolver, the ShouldSerialize predicate and the NullValueHandling property. This link was very useful. The properties are stored in a Dictionary in a base class ApiModel; that code is straightforward.
Account Model
[JsonProperty("name")]
public string Name
{
get { return this.GetAttributeValue<string>("name"); }
set { this.SetAttributeValue<string>("name", value); }
}
Json Serialization
ApiModel.JsonSerializerSettings = new Newtonsoft.Json.JsonSerializerSettings();
ApiModel.JsonSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include;
ApiModel.JsonSerializerSettings.ContractResolver = ApiModel.JsonContractResolver;
internal class ApiModelContractResolver : Newtonsoft.Json.Serialization.DefaultContractResolver
{
protected override Newtonsoft.Json.Serialization.JsonProperty CreateProperty(System.Reflection.MemberInfo member, Newtonsoft.Json.MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.ShouldSerialize =
instance =>
{
var apiModel = instance as ApiModel;
var hasAttribute = apiModel.HasAttribute(property.PropertyName);
property.NullValueHandling = hasAttribute ? Newtonsoft.Json.NullValueHandling.Include : Newtonsoft.Json.NullValueHandling.Ignore;
return hasAttribute;
};
return property;
}
}
We are having an issue with searching a custom record through SuiteTalk. Below is a sample of what we are calling. The issue we are having is in trying to set up the search using the internalId of the record. The issue here lies in in our initial development account the internal id of this custom record is 482 but when we deployed it through the our bundle the record was assigned with the internal Id of 314. It would stand to reason that this internal id is not static in a site per site install so we wondered what property to set up to reference the custom record. When we made the record we assigned its “scriptId’ to be 'customrecord_myCustomRecord' but through suitetalk we do not have a “scriptId”. What is the best way for us to allow for this code to work in all environments and not a specific one? And if so, could you give an example of how it might be used.
Code (C#) that we are attempting to make the call from. We are using the 2013.2 endpoints at this time.
private SearchResult NetSuite_getPackageContentsCustomRecord(string sParentRef)
{
List<object> PackageSearchResults = new List<object>();
CustomRecord custRec = new CustomRecord();
CustomRecordSearch customRecordSearch = new CustomRecordSearch();
SearchMultiSelectCustomField searchFilter1 = new SearchMultiSelectCustomField();
searchFilter1.internalId = "customrecord_myCustomRecord_sublist";
searchFilter1.#operator = SearchMultiSelectFieldOperator.anyOf;
searchFilter1.operatorSpecified = true;
ListOrRecordRef lRecordRef = new ListOrRecordRef();
lRecordRef.internalId = sParentRef;
searchFilter1.searchValue = new ListOrRecordRef[] { lRecordRef };
CustomRecordSearchBasic customRecordBasic = new CustomRecordSearchBasic();
customRecordBasic.recType = new RecordRef();
customRecordBasic.recType.internalId = "314"; // "482"; //THIS LINE IS GIVING US THE TROUBLE
//customRecordBasic.recType.name = "customrecord_myCustomRecord";
customRecordBasic.customFieldList = new SearchCustomField[] { searchFilter1 };
customRecordSearch.basic = customRecordBasic;
// Search for the customer entity
SearchResult results = _service.search(customRecordSearch);
return results;
}
I searched all over for a solution to avoid hardcoding internalId's. Even NetSuite support failed to give me a solution. Finally I stumbled upon a solution in NetSuite's knowledgebase, getCustomizationId.
This returns the internalId, scriptId and name for all customRecord's (or customRecordType's in NetSuite terms! Which is what made it hard to find.)
public string GetCustomizationId(string scriptId)
{
// Perform getCustomizationId on custom record type
CustomizationType ct = new CustomizationType();
ct.getCustomizationTypeSpecified = true;
ct.getCustomizationType = GetCustomizationType.customRecordType;
// Retrieve active custom record type IDs. The includeInactives param is set to false.
GetCustomizationIdResult getCustIdResult = _service.getCustomizationId(ct, false);
foreach (var customizationRef in getCustIdResult.customizationRefList)
{
if (customizationRef.scriptId == scriptId) return customizationRef.internalId;
}
return null;
}
you can make the internalid as an external property so that you can change it according to environment.
The internalId will be changed only when you install first time into an environment. when you deploy it into that environment, the internalid will not change with the future deployments unless you choose Add/Rename option during deployment.
I've added a boolean parameter called IsNewReport to some of my reports and I'm trying get a list of these reports using the SSRS web service (ReportService2005.asmx).
But I keep getting an exception thrown by the web service "The IsNewReport field has a value that is not valid."
So how should I setup the SearchCondition to find my reports?
Heres an example of the webservice call:
var reports = ReportingService2005.FindItems("/MyReports",
BooleanOperatorEnum.Or, GetSearchConditions());
And here's an example of the GetSearchCondition method
private static SearchCondition[] GetSearchConditions()
{
List<SearchCondition> conditions = new List<SearchCondition>();
SearchCondition searchCondition = new SearchCondition();
searchCondition.Condition = ConditionEnum.Equals;
searchCondition.ConditionSpecified = true;
searchCondition.Name = "IsNewReport";
searchCondition.Value = "true";
conditions.Add(searchCondition);
return conditions.ToArray();
}
PS I've tried this code changing the property name to "Name" and a value matching the name of one the existing reports and this works without exception,.
Ok I've got it - you can't use FindItems to search though a reports parameters only the properties of the report. "Name" worked because it's a property of the report where as IsNewReport is a parameter that I've added to the report.