Can a proto3 scalar field be changed to optional without breaking wire compatibility? - compatibility

Can I change a scalar field in proto3 to optional, e.g.
message UpdateAccountRequest {
string name = 1;
string country = 2;
}
to
message UpdateAccountRequest {
optional string name = 1;
optional string country = 2;
}
without breaking anything? That is, without affecting clients who are currently always setting those fields.

Related

Serilog Custom Sink Formatting Issue with Serilog LogEventPropertyValue

I'm having to create my own custom sink because none of the ones currently available give me what I need.
Issue I have is when fetching the key/value pair Value from the logEvent message in the Emit Method, the value is wrapped with quotation marks & backslashes.
I've tried converting the out value from the dictionary into a string and then removing the unwanted attributes but nothing is working for me.
Method in my Custom Sink Class:
public void Emit(LogEvent logEvent)
{
var properties = logEvent.Properties;
Serilog.Events.LogEventPropertyValue value;
if (properties.TryGetValue("logEventCategory", out value))
{
// Regex.Replace((value.ToString() ?? "").Replace("'", #"\'").Trim(), #"[\r\n]+", " "); // Not working
var notWorking = value.ToString();
var formattedValueNotWorking = value.ToString().Replace("\r\n", "\\r\\n");
}
}
It just seems that any attempted formatting of the key/value pair Value is ignored: You see that the example string value System is wrapped with a \"System\"
All I want is the actual string, not the backslashes or quotation marks that is wrapped around the string.
Creating my own sink is a hard enough task and I just want to keep things simple, have spent two days trying to understand the wider picture in message formatting but with custom sinks it gets too complicated and bloated coding for what I need. All the other standard message structure attributes are rendering OK, such as message / level / timestamp etc, it's just fine tuning the rendering of the propertie values I require in order to save these values into their own columns in my DB.
You need to unwrap the string from the enclosing ScalarValue:
// using Serilog.Events;
public void Emit(LogEvent logEvent)
{
var properties = logEvent.Properties;
Serilog.Events.LogEventPropertyValue value;
if (properties.TryGetValue("logEventCategory", out value) &&
value is ScalarValue sv &&
sv.Value is string rawValue)
{
// `rawValue` is what you're looking for
Looks like I just needed to use the correct syntax for string replace:
public void Emit(LogEvent logEvent)
{
var properties = logEvent.Properties;
Serilog.Events.LogEventPropertyValue value;
if (properties.TryGetValue("logEventCategory", out value))
{
var formattedValueWorking = value.ToString().Replace("\"", "");
var test = formattedValueWorking;
}
}

Add metadata to fields in proto3 for Java

Proto3 has been simplified such that the required and optional fields are no longer supported (See Why required and optional is removed in Protocol Buffers 3). Is there still a way to label a certain field as required?
I've looked into FieldOptions, and tried something like this:
message MyMeta {
bool isRequired = 1;
}
extend google.protobuf.FieldOptions {
MyMeta meta = 1234;
}
message Person {
string name = 1 [ (meta) = { isRequired: true }];
string address = 2 [ (meta) = { isRequired: true }];
string remarks = 3;
}
After compiling it into Java code, and as I was checking the compiled Java code I don't see any link between the fields and its metadata I specified in proto. Did I miss something here?
After a bit of tinkering and using #Eric Anderson's idea on using proto reflection, here is a way to retrieve MyMeta from the Person.name field:
Descriptor rootDesc = PersonProto.getDescriptor();
FieldDescriptor name = rootDesc.findFieldByName("name");
FieldDescriptor ext = rootDesc.getFile().getExtensions().get(0);
MyMeta meta = (MyMeta) name.getOptions().getField(ext);
boolean isReq = meta.getIsRequired();
No, that functionality was removed; use documentation instead. If you are trying to use FieldOptions for your own extensions, then you can make your own protoc plugin to generate supplemental code (like a verify utility) or use proto reflection at runtime (via FooMessage.getDescriptor() and Descriptors.FieldDescriptor.getOptions() in Java).

How to get the Guids and mobilephone from a Party List?

I'm making a windows service that triggers on the create message of a custom activity SMS. These program will send the actual sms using a third party sms service provider.
Therefore I need to get the mobilephone numbers for every contact/lead/user/account in the "To" field of the SMS activity. This is a field of type: Party List.
In addition I have another field ("new_msisdn") which I use it if "to" field be empty.(In this field user will type phone numbers directly)
I'm currently using the following code:
EntityCollection results = CrmService.RetrieveMultiple(query);
string msisdn;
string newmessage;
Entity entity;
int encode;
bool flash;
res = new Message[results.Entities.Count];
if (results != null && results.Entities.Count > 0)
{
try
{
for (int i = 0; i < results.Entities.Count; i++)
{
entity = results.Entities[i];
msisdn = (string)entity["new_msisdn"];
// I have to add an condition here to check if "to" is not empty , then get mobilephones.
newmessage = (string)entity["new_message"];
encode = ((OptionSetValue)entity["new_messagetype"]).Value;
flash = (bool)entity["new_flashsms"];
res[i] = new Message();
res[i].crmId = entity.Id;
res[i].senderNumber = msisdn;
res[i].sendDate = DateTime.Now;
res[i].message = newmessage;
if (encode == 1)
res[i].encoding = 1;
else
res[i].encoding = 2;
if (flash)
res[i].flash = 2;
else res[i].flash = 1;
}
}
I have no ideas to do this. By the way, I use CRM 2015.
Try to use something like below.
if(entity.Attributes.Contains("to"))
{
EntityCollection to=(EntityCollection)entitiy["to"];
foreach(Entity toEntity in to.Entities)
{
//You will get each to field record here.
//Use that information to get the mobile numbers of respective users.
Guid Id=toEntity.Id;
//Below code mostly will return string.empty.
//You may have to query CRM to get mobile number for respective contacts.
string mobileNumber=toEntity.Attributes.Contains("mobilenumber")?toEntity["mobilenumber"].ToString():string.Empty;
}
}
Hope I have addressed your query. Please let us know if you have any further questions on the same.
Also its a best practice to check whether attribute is already available in the entity object or not before using a particular attribute.

HTTP Header Key can be repeated?

In JAVA HttpUrlConnection , the main logic code of request Header settings as following:
public synchronized void set(String k, String v) {
for (int i = nkeys; --i >= 0;)
if (k.equalsIgnoreCase(keys[i])) {
values[i] = v;
return;
}
add(k, v);
}
It is verified that the key should be unique, the key has to keep one to one mapping relationship with the value.
On the contrary, in HeaderFields of Response module, structure is defined as Entry >. That is, the key does not keep one to one mapping relationship with the value.
Why is this? Does the HTTP protocol has relevant agreement?
Add:
In HttpClient4 ,the main logic code of request Header settings as following:
/**
* Replaces the first occurence of the header with the same name. If no header with
* the same name is found the given header is added to the end of the list.
*
* #param header the new header that should replace the first header with the same
* name if present in the list.
*/
public void updateHeader(final Header header) {
if (header == null) {
return;
}
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header current = this.headers.get(i);
if (current.getName().equalsIgnoreCase(header.getName())) {
this.headers.set(i, header);
return;
}
}
this.headers.add(header);
}
Header of response
/**
* Gets all of the headers with the given name. The returned array
* maintains the relative order in which the headers were added.
*
* <p>Header name comparison is case insensitive.
*
* #param name the name of the header(s) to get
*
* #return an array of length >= 0
*/
public Header[] getHeaders(final String name) {
final List<Header> headersFound = new ArrayList<Header>();
// HTTPCORE-361 : we don't use the for-each syntax, i.e.
// for (Header header : headers)
// as that creates an Iterator that needs to be garbage-collected
for (int i = 0; i < this.headers.size(); i++) {
final Header header = this.headers.get(i);
if (header.getName().equalsIgnoreCase(name)) {
headersFound.add(header);
}
}
return headersFound.toArray(new Header[headersFound.size()]);
}
They are the same of HttpUrlConnection
Does the HTTP protocol has relevant agreement?
Yes. RFC 2616 Section 4.2 "Message Headers" says:
Multiple message-header fields with the same field-name MAY be
present in a message if and only if the entire field-value for that
header field is defined as a comma-separated list [i.e., #(values)].
It MUST be possible to combine the multiple header fields into one
"field-name: field-value" pair, without changing the semantics of the
message, by appending each subsequent field-value to the first, each
separated by a comma. The order in which header fields with the same
field-name are received is therefore significant to the
interpretation of the combined field value, and thus a proxy MUST NOT
change the order of these field values when a message is forwarded.
This is expanded further by RFC 7230 Section 3.2.2 "Field Order":
A sender MUST NOT generate multiple header fields with the same field
name in a message unless either the entire field value for that
header field is defined as a comma-separated list [i.e., #(values)]
or the header field is a well-known exception (as noted below).
A recipient MAY combine multiple header fields with the same field
name into one "field-name: field-value" pair, without changing the
semantics of the message, by appending each subsequent field value to
the combined field value in order, separated by a comma. The order
in which header fields with the same field name are received is
therefore significant to the interpretation of the combined field
value; a proxy MUST NOT change the order of these field values when
forwarding a message.

SQL statement's placeholders that is not replaced leads to "Cannot update '#columnName'; field not updateable"

I'm writing some code updating database with a SQL statement that has some placeholders . But it doesn't seem to update these placeholders.
I got the following error:
Cannot update '#columnName'; field not updateable
Here is the method:
public void updateDoctorTableField(string columnName, string newValue, string vendorNumber) {
sqlStatement = "update Doctor set #columnName = #newValue where `VENDOR #` = #vendorNumber;";
try {
_command = new OleDbCommand(sqlStatement, _connection);
_command.Parameters.Add("#columnName", OleDbType.WChar).Value = columnName;
_command.Parameters.Add("#newValue", OleDbType.WChar).Value = newValue;
_command.Parameters.Add("#vendorNumber", OleDbType.WChar).Value = vendorNumber;
_command.ExecuteNonQuery();
} catch (Exception ex) {
processExeption(ex);
} finally {
_connection.Close();
}
}
Not all parts of the query are parameterisable.
You can't parametrise the name of the column. This needs to be specified explicitly in your query text.
If this is sent via user input you need to take care against SQL Injection. In fact in any event it would be best to check it against a whitelist of known valid column names.
The reason the language does not allow for parameters for things like table names, column names and such is exactly the same reason why your C# program does not allow for substitution of variables in the code. Basically your question can be rephrased like this in a C# program:
class MyClass
{
int x;
float y;
string z;
void DoSomething(string variableName)
{
this.#variable = ...
}
}
MyCLass my = new MyClass();
my.DoSomething("x"); // expect this to manuipulate my.x
my.DoSomething("y"); // expect this to manuipulate my.y
my.DoSomething("z"); // expect this to manuipulate my.z
This obviously won't compile, because the compiler cannot generate the code. Same for T-SQL: the compiler cannot generate the code to locate the column "#columnName" in your case. And just as in C# you would use reflection to do this kind of tricks, in T-SQL you would use dynamic SQL to achieve the same.
You can (and should) use the QUOTENAME function when building your dynamic SQL to guard against SQL injection.

Resources