When executing code that uses the Google Sheets API to sort a sheet A to Z, I get an error that I don't know how to fix.
Message[Invalid requests[0].sortRange: No sort order specified.] Location[ - ] Reason[badRequest] Domain[global]
Using this code, it should have created sort order and dimension index, and put it into SortSpec, which was added to SortRange, but it doesn't seem like Google Sheets recognizes that.
List<Data.Request> requests = new List<Data.Request>(); // TODO: Update placeholder value.
Data.SortSpec so = new Data.SortSpec();
so.SortOrder = "ASCENDING";
Data.SortSpec di = new Data.SortSpec();
di.DimensionIndex = 0;
List<Data.SortSpec> ss = new List<Data.SortSpec>();
ss.Add(so);
ss.Add(di);
var test = new Request()
{
SortRange = new SortRangeRequest()
{
Range = new GridRange()
{
SheetId = 0,
StartRowIndex = 1
},
SortSpecs = ss
}
};
requests.Add(test);
// TODO: Assign values to desired properties of `requestBody`:
Data.BatchUpdateSpreadsheetRequest requestBody = new Data.BatchUpdateSpreadsheetRequest();
requestBody.Requests = requests;
SpreadsheetsResource.BatchUpdateRequest request = service.Spreadsheets.BatchUpdate(requestBody, spreadsheetId);
Data.BatchUpdateSpreadsheetResponse response = request.Execute();
Using debug within Visual Studio, I looked through Autos to see what values request had for SortSpecs, and I found that both values were there.
- [0] {Google.Apis.Sheets.v4.Data.SortSpec} Google.Apis.Sheets.v4.Data.SortSpec
DimensionIndex null int?
ETag null string
SortOrder "ASCENDING" string
- [1] {Google.Apis.Sheets.v4.Data.SortSpec} Google.Apis.Sheets.v4.Data.SortSpec
DimensionIndex 0 int?
ETag null string
SortOrder null string
Answer was very simple. Instead of creating seperate sortspecs within the list, you must define dimension index and sort order to the same variable
Data.SortSpec so = new Data.SortSpec();
so.SortOrder = "ASCENDING";
so.DimensionIndex = 0;
List<Data.SortSpec> ss = new List<Data.SortSpec>();
ss.Add(so);
Related
If I simply attempt to add a Group using the Kontent Management API (v2) to an existing Kontent type I get the following error (see code below which generates this error):
Validation errors: 511 Every element should have a correct content group reference when using groups
What is the process for adding a group via the Management API in this case using C#? I would assume that I need to add a group Reference to all the existing elements first, but how do I do that when I cannon add the group through the API? Can I create a valid Reference simply using a new ContentGroupModel() object before adding it to the actual type in Kontent?
Here is my existing code, which throws the above error:
var updatedType = await service.ModifyContentTypeAsync(
Reference.ById(existingContentType.Id),
new ContentTypeAddIntoPatchModel
{
Path = "/content_groups",
Value = new ContentGroupModel
{
Name = "New Group",
CodeName = "new_group"
}
}
);
using Kentico.Kontent.Management;
var client = new ManagementClient(new ManagementOptions
{
ApiKey = "<YOUR_API_KEY>",
ProjectId = "<YOUR_PROJECT_ID>"
});
var identifier = Reference.ById(Guid.Parse("0be13600-e57c-577d-8108-c8d860330985"));
// var identifier = Reference.ByCodename("my_article");
// var identifier = Reference.ByExternalId("my-article-id");
var response = await client.ModifyContentTypeAsync(identifier, new ContentTypeOperationBaseModel[]
{
new ContentTypeReplacePatchModel
{
Path = "/name",
Value = "A new type name"
},
new ContentTypeReplacePatchModel
{
Path = "/elements/codename:my_text_element/guidelines",
Value = "Here you can tell users how to fill in the element."
},
new ContentTypeAddIntoPatchModel
{
Path = "/elements",
Value = new TextElementMetadataModel
{
Name = "My title",
Guidelines = "Title of the article in plain text.",
ExternalId = "my-title-id",
},
},
new ContentTypeRemovePatchModel
{
Path = "/elements/id:0b2015d0-16ae-414a-85f9-7e1a4b3a3eae"
}
});
Refer - https://docs.kontent.ai/reference/management-api-v2#operation/modify-a-content-type
I was able to work it out. It turns out you can use the Codename of the new group, even before adding it in Kontent, as a reference. Then give that reference to the content_group property/path for existing elements in the model.
Here is the code I have now that adds the new Group and adds it to all existing elements dynamically:
using Kentico.Kontent.Management.Models.Shared;
using Kentico.Kontent.Management.Models.Types;
using Kentico.Kontent.Management.Models.Types.Patch;
//
// ...
//
// First, Get type from the client. Then ...
var elementCodenames = myType.Elements.Select(el => el.Codename);
// Create reference to the codename of the group to be created
var groupRef = Reference.ByCodename("new_group");
// Create the modify models
var modifyModels = new ContentTypeOperationBaseModel[] {
new ContentTypeAddIntoPatchModel
{
Path = "/content_groups", // Add to content_groups path
Value = new ContentGroupModel
{
Name = "New Group",
CodeName = "new_group" // Same codename as above
}
}
}
.Concat(
// Dynamically add new group, by ref, to existing elements
elementCodenames.Select(codename => new ContentTypeReplacePatchModel
{
Path = $"/elements/codename:{codename}/content_group",
Value = groupRef // Group reference created above from the codename
})
);
// Make call to add new group AND link group to elements in the one call
var response = await client.ModifyContentTypeAsync(myTypeIdentifier, modifyModels.ToArray());
Leaving out other details, like retrieving the existing type, but for reference here are the API Docs: https://docs.kontent.ai/reference/management-api-v2
I can't find an example of this here or in the Acumatica sample code. Sending single attribute values works fine, but I can't find a way to send multi-select ones. They are returned as a comma-separated list in a string value, but sending them that way doesn't work. Also, sending them as multiple instances of single values doesn't work.
Here's what I've tried. (In the actual code I'm sending some other single attributes in the list, as well, but those work fine.)
// this results in nothing being set for the attribute
string interestedIn = "Brochure, Contact, Collecting small stones";
List<Acumatica.AttributeDetail> attributes = new List<Acumatica.AttributeDetail>();
attributes.Add(
new Acumatica.AttributeDetail {
Attribute = new Acumatica.StringValue { Value = "Interested in" },
Value = new Acumatica.StringValue { Value = interestIn }
}
);
custAdd.Attributes = attributes.ToArray();
// this results in the last item in the list being set for the attribute
string interestedIn = "Brochure, Contact, Collecting small stones";
List<Acumatica.AttributeDetail> attributes = new List<Acumatica.AttributeDetail>();
string[] interests = Convert.ToString(interestedIn).Split(',');
foreach (string interest in interests) {
attributes.Add(
new Acumatica.AttributeDetail {
Attribute = new Acumatica.StringValue { Value = "Interested in" },
Value = new Acumatica.StringValue { Value = interest.Trim() }
}
);
};
custAdd.Attributes = attributes.ToArray();
From the source
MappedCustomer obj = bucket.Customer;
Core.API.Customer impl = obj.Local;
impl.Attributes = impl.Attributes ?? new List<AttributeValue>();
AttributeValue attribute = new AttributeValue();
attribute.AttributeID = new StringValue() { Value = attributeID };
attribute.ValueDescription = new StringValue() { Value = attributeValue?.ToString() };
impl.Attributes.Add(attribute);
Some subtle differences here. Also, I wonder if the .ToArray() call is necessary.
WSDL 2014.2. C# in Visual Studio 2013.
I can get "Inventory Item" items using this code:
var recordRefs = new List<RecordRef>();
foreach (string externalId in ExternalIds)
{
recordRefs.Add(new RecordRef
{
externalId = externalId,
type = RecordType.inventoryItem,
typeSpecified = true
});
}
var request = new ItemSearchBasic
{
externalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = recordRefs.ToArray()
}
};
SearchResult response = SuiteTalkConnection.Service.search(request);
However, if I change type = RecordType.inventoryItem to type = RecordType.lotNumberedAssemblyItem then I don't get results. I am specifying correct values as external IDs.
I have tried all sorts of variations on the above, including ItemSearch instead of ItemSearchBasic but I never get results.
It doesn't help that I can find zero NetSuite documentation on searching for specific item types.
I think answer to this question would also work in this case. Try using type as "assemblyItem" only and use an additional searchFilter "isLotItem" and set it to true.
I'm trying to complete fields with JoeBlogs WordPress Wrapper.
My code is:
private void postToWordpress(string title, string postContent,string tags, string aioTitle)
{
string link = this.maskedTextBox1.Text;
string username = this.maskedTextBox2.Text;
string password = this.maskedTextBox3.Text;
var wp = new WordPressWrapper(link + "/xmlrpc.php", username, password);
var post = new Post();
post.Title = title;
post.Body = postContent;
post.Tags = tags.Split(',');
string[] cf = new CustomField(); //{ ID = "name", Key = "aiosp_title", Value = "All in One SEO Title" };
cf.ID = "name";
cf.Key = "aiosp_title";
cf.Value = "All in One SEO Title";
post.CustomFields[0] = cf;
wp.NewPost(post, false);
}
The error is at this line:
post.CustomFields[0] = cf;
And it is:
An unhandled exception of type 'System.NullReferenceException'
occurred in JoeBlogsWordpressWrapperTests.exe
Additional information: Object reference not set to an instance of an
object.
So, how to use/add correctly custom fields on WordPress from C# Application using JoeBlogs WordPress Wrapper?
The following code fixes your NullReferenceException and also successfully saves the custom fields into the Post in Wordpress.
private void postToWordpress(string title, string postContent,string tags, string aioTitle)
{
string link = this.maskedTextBox1.Text;
string username = this.maskedTextBox2.Text;
string password = this.maskedTextBox3.Text;
var wp = new WordPressWrapper(link + "/xmlrpc.php", username, password);
var post = new Post();
post.Title = title;
post.Body = postContent;
post.Tags = tags.Split(',');
var cfs = new CustomField[]
{
new CustomField()
{
// Don't pass in ID. It's auto assigned for new custom fields.
// ID = "name",
Key = "aiosp_title",
Value = "All in One SEO Title"
}
};
post.CustomFields = cfs;
wp.NewPost(post, false);
}
You were getting the NullReferenceException error because you were creating a string array and trying to assign it the CustomFields property of the Post object, which is an array of CustomField i.e. CustomField[].
Also, in order to save the CustomFields to the Post in the database, you should pass in only the Key and Value fields of the CustomField struct and skip the ID field all together. Reason being Wordpress auto-generates the ID fields (also it's an integer / numeric field in the database). I think that was what was causing the XmlRpc call to fail, but we did not get any errors as to why.
Try the above code and it should work (I have it working on my localhost WAMP Wordpress installation).
One final note. Although the CustomField's name property is called Key, it doesn't have to be unique, and uniqueness is not enforced. So for instance, if you are populating a custom dropdown box with a list of cities for a Post, you could have the list of cities as a set of custom fields as follows.
var cfs = new CustomField[]
{
new CustomField()
{
Key = "aiosp_title",
Value = "All in One SEO Title"
} ,
new CustomField()
{
Key = "this is another custom field with HTML",
Value = "All in One SEO Title <br/> Keyword 1 <br/><p>This is some more text and html</p>"
} ,
new CustomField()
{
Key = "list_of_cities",
Value = "San Francisco"
} ,
new CustomField()
{
Key = "list_of_cities",
Value = "New York"
}
};
This will also get saved to the post, with 2 custom fields with the same Key value and different text in the Value field's value.
And last but not least, you can store HTML also in the custom fields (as shown above).
Cannot find any documentation for this...
Currently using the following code to get a list of my photos:
FacebookApp fb = new FacebookApp(accessToken);
dynamic test = fb.Get("me/photos");
I'm cycling through the first 25 photos that it returns. Simple.
Now how do it get it to return the next 25?
So far I've tried this:
FacebookApp fb = new FacebookApp(accessToken);
string query = "me/photos";
while (true)
{
dynamic test = fb.Get(query);
foreach (dynamic each in test.data)
{
// do something here
}
query = test.paging.next;
}
but it fails throwing:
Could not parse '2010-08-30T17%3A58%3A56%2B0000' into a date or time.
Do I have to use a fresh dynamic variable for every request, or am I going about this the wrong way completely?
Ended up finding this:
// first set (1-25)
var parameters = new ExpandoObject();
parameters.limit = 25;
parameters.offset = 0;
app.Api("me/friends", parameters);
// next set (26-50)
var parameters = new ExpandoObject();
parameters.limit = 25;
parameters.offset = 25;
app.Api("me/friends", parameters);
I also found you can use this.
// for the first 25 albums (in this case) 1-25
dynamic albums = client.Get("me/albums", new { limit = "25", offset = "0"});
// for the next 25 albums, 26-50
dynamic albums = client.Get("me/albums", new { limit = "25", offset = "25"});
Worked the same as you used above.