I have this rpc in my .proto file:
message SetFlagRequest {
string name = 1;
bool enable = 2;
}
rpc SetFeatureFlag(SetFlagRequest) returns (FlagStatus) {
option (google.api.http) = {
post: "/v2/flags/{name}/{enable}"
body: "*"
};
//...
I would like to have post address of:
"/v2/flags/{name}/enable" (for enable = true) instead of "/v2/flags/{name}/true"
"/v2/flags/{name}/disable" (for enable = false) instead of "/v2/flags/{name}/false"
is there a way of string formatting in .proto files?
Nope, standard practice is to make them explicit.
message SetFlagRequest {
string name = 1;
}
rpc EnableFeatureFlag(SetFlagRequest) returns (FlagStatus) {
option (google.api.http) = {
post: "/v2/flags/{name}/enable"
body: "*"
};
rpc DisableFeatureFlag(SetFlagRequest) returns (FlagStatus) {
option (google.api.http) = {
post: "/v2/flags/{name}/disable"
body: "*"
};
Then in your implementation of each you can call a common method if you like.
Related
We are seeing an issue with users unable to access our production and PPE apps via LinkedIn sign in. The redirection is not happening to specified redirect URL once users provides user name and password. The network trace shows login is successful but not going to redirect URL. This has been working last 4 years or so and suddenly started failing in both environments from yesterday.
Bummer. Something went wrong
We tried verifying the network trace and a support case is raised to LinkedIn with recording. Finally we are redirected to raise the issue here.
I had the same issue and found that it was caused by using JSON.stringify to "overload" the state parameter with other parameters. In my case, I add other parameters in the following way:
providerCfg.auth_params.state = JSON.stringify({
state: providerCfg.auth_params.state,
redirectPageUrl,
redirectParams,
userTypeBit,
isLogin
})
const authUrl = new URL(providerCfg.auth_url)
Object.entries(providerCfg.auth_params).forEach(([key, val]) => {
authUrl.searchParams.append(key, encodeURIComponent(val))
})
return buildURL(providerCfg.auth_url, providerCfg.auth_params)
When I removed the call to JSON.stringify and just passed in a state parameter, the oauth flow worked correctly. Obviously, the other parameters that I passed in were important so I created my own functions to serialize and deserialize the values. The code below works well for anything other than deeply nested objects. You will need to update the metaDataCfg based on your own requirements.
const META_STRING_DELIMITER = '|'
const serializeBasicObject = (targetObj) => {
if (!targetObj) {
return ''
}
return Object.entries(targetObj).reduce((objString, [key, val]) => {
const param = `${key}=${val || ''}`
if (!objString.length) {
return param
}
return `${objString}${META_STRING_DELIMITER}${param}`
}, '')
}
const deserializeBasicObject = (targetStr) => {
if (!targetStr) {
return ''
}
const keyValPairs = targetStr.split(META_STRING_DELIMITER)
return keyValPairs.reduce((targetObj, keyValPair) => {
const splitIdx = keyValPair.indexOf('=')
const key = keyValPair.slice(0, splitIdx)
targetObj[key] = keyValPair.slice(splitIdx + 1, keyValPair.length)
return targetObj
}, {})
}
const metaDataCfg = {
state: {},
redirectPageUrl: {},
redirectParams: {
serialize: serializeBasicObject,
deserialize: deserializeBasicObject
},
userTypeBit: { deserialize: Number },
isLogin: { deserialize: dataUtil.getBoolean }
}
const getMetaString = (metaData) => {
return Object.entries(metaDataCfg).reduce((metaString, [metaDataKey, cfg]) => {
const val = (cfg.serialize) ? cfg.serialize(metaData[metaDataKey]) : metaData[metaDataKey]
const param = `${metaDataKey}=${dataUtil.isNil(val) ? '' : val}`
if (!metaString.length) {
return param
}
return `${metaString}${META_STRING_DELIMITER}${param}`
}, '')
}
export const getDataFromMetaString = (metaString) => {
const params = metaString.split(META_STRING_DELIMITER)
const data = params.reduce((metaData, param) => {
const splitIdx = param.indexOf('=')
const key = param.slice(0, splitIdx)
let val = param.slice(splitIdx + 1, param.length)
if (dataUtil.isNil(val) || !val.length) {
return metaData
}
const deserializer = metaDataCfg[key].deserialize
if (deserializer && val) {
val = deserializer(val)
}
metaData[key] = val
return metaData
}, {})
return data
}
The following code by #Nick Rupley works well, but, I need also to pass parameters as POST. How do we pass POST parameters?
from java.net.URL
var url = new java.net.URL('http://localhost/myphpscript.php');
var conn = url.openConnection();
var is = conn.getInputStream();
try {
var result = org.apache.commons.io.IOUtils.toString(is, 'UTF-8');
} finally {
is.close();
}
2 Parameters to pass: firstname="John" and lastname="Smith"
Thanks
This will POST with MIME type application/x-www-form-urlencoded. It is using apache httpclient, which is already included with mirth, as it is used internally by the HTTP Sender connector, as well as some other functionality. Other solutions may require you to download jars and add library resources.
Closer is part of Google Guava, which is also already included with mirth.
Check comments where Rhino javascript allows for simplified code compared to direct Java conversion.
It wouldn't be a bad idea to wrap all of this up in a code template function.
var result;
// Using block level Java class imports
with (JavaImporter(
org.apache.commons.io.IOUtils,
org.apache.http.client.methods.HttpPost,
org.apache.http.client.entity.UrlEncodedFormEntity,
org.apache.http.impl.client.HttpClients,
org.apache.http.message.BasicNameValuePair,
com.google.common.io.Closer))
{
var closer = Closer.create();
try {
var httpclient = closer.register(HttpClients.createDefault());
var httpPost = new HttpPost('http://localhost:9919/myphpscript.php');
// javascript array as java List
var postParameters = [
new BasicNameValuePair("firstname", "John"),
new BasicNameValuePair("lastname", "Smith")
];
// Rhino JavaBean access to set property
// Same as httpPost.setEntity(new UrlEncodedFormEntity(postParameters, "UTF-8"));
httpPost.entity = new UrlEncodedFormEntity(postParameters, "UTF-8");
var response = closer.register(httpclient.execute(httpPost));
// Rhino JavaBean access to get properties
// Same as var is = response.getEntity().getContent();
var is = closer.register(response.entity.content);
result = IOUtils.toString(is, 'UTF-8');
} finally {
closer.close();
}
}
logger.info(result);
Following is a complete working HTTP POST request solution tested in Mirth 3.9.1
importPackage(Packages.org.apache.http.client);
importPackage(Packages.org.apache.http.client.methods);
importPackage(Packages.org.apache.http.impl.client);
importPackage(Packages.org.apache.http.message);
importPackage(Packages.org.apache.http.client.entity);
importPackage(Packages.org.apache.http.entity);
importPackage(Packages.org.apache.http.util);
var httpclient = HttpClients.createDefault();
var httpPost = new HttpPost("http://localhost/test/");
var httpGet = new HttpGet("http://httpbin.org/get");
// FIll in each of the fields below by entering your values between the ""'s
var authJSON = {
"userName": "username",
"password": "password",
};
var contentStr =JSON.stringify(authJSON);
//logger.info("JSON String: "+contentStr);
httpPost.setEntity(new StringEntity(contentStr,ContentType.APPLICATION_JSON,"UTF-8"));
httpPost.setHeader('Content-Type', 'application/json');
httpPost.setHeader("Accept", "application/json");
// Execute the HTTP POST
var resp;
try {
// Get the response
resp = httpclient.execute(httpPost);
var statusCode = resp.getStatusLine().getStatusCode();
var entity = resp.getEntity();
var responseString = EntityUtils.toString(entity, "UTF-8");
var authHeader = resp.getFirstHeader("Authorization");
// logger.info("Key : " + authHeader.getName()+" ,Value : " + authHeader.getValue());
// Save off the response and status code to Channel Maps for any potential troubleshooting
channelMap.put("responseString", responseString);
channelMap.put("statusCode", statusCode);
// Parse the JSON response
var responseJson = JSON.parse(responseString);
// If an error is returned, manually throw an exception
// Else save the token to a channel map for use later in the processing
if (statusCode >= 300) {
throw(responseString);
} else {
logger.info("Token: "+ authHeader.getValue());
channelMap.put("token", authHeader.getValue());
}
} catch (err) {
logger.debug(err)
throw(err);
} finally {
resp.close();
}
This linke + above answers helped me to come up with a solution
https://help.datica.com/hc/en-us/articles/115005322946-Advanced-Mirth-Functionality
There are plenty of libraries that can help you with URI building in Java. You can find them below. But if you want to stay in Javascript just add your parameters manually than create it.
function addParam(uri, appendQuery) {
if (appendQuery != null) {
uri += "?" + appendQuery;
}
return uri;
}
var newUri = addParam('http://localhost/myphpscript.php', 'firstname="John"');
var url = new java.net.URL(newUri);
Java EE 7
import javax.ws.rs.core.UriBuilder;
...
return UriBuilder.fromUri(url).queryParam(key, value).build();
org.apache.httpcomponents:httpclient:4.5.2
import org.apache.http.client.utils.URIBuilder;
...
return new URIBuilder(url).addParameter(key, value).build();
org.springframework:spring-web:4.2.5.RELEASE
import org.springframework.web.util.UriComponentsBuilder;
...
return UriComponentsBuilder.fromUriString(url).queryParam(key, value).build().toUri();
There are multiple ways to provide http client connection with java. Since your question is specific to java.net.URL I will stick to that.
Basically you can pass parameters as POST, GET, PUT, DELETE using .setRequestMethod this will be used along with new java.net.URL(ur-destination-url).openConnection();
Here is the complete code I've using javascript in Mirth using the same java.net.URL use this it will be helpful. It worked well for me.
do {
try {
// Assuming your writing this in the destination Javascript writer
var data = connectorMessage.getEncodedData();
//Destination URL
destURL = “https://Your-api-that-needs-to-be-connected.com”;
//URL
var url = new java.net.URL(destURL);
var conn = url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
enter code here
conn.setRequestProperty (“Authorization”, globalMap.get(‘UniversalToken’));
conn.setRequestMethod(“DELETE”); // this can be post or put or get or patch
conn.setRequestProperty(“Content-length”, data.length());
conn.setRequestProperty(“Content-type”, “application/json”);
var outStream = conn.getOutputStream();
var outWriter = new java.io.OutputStreamWriter(outStream);
outWriter.write(data);
outWriter.close();
// Get response Code (200, 500 etc.)
var respCode = conn.getResponseCode();
if (respCode != 200) {
// Write error to error folder
var stringData = response.toString() + “\n”;
FileUtil.write(“C:/Outbox/Errors/” + $(“originalFilename”) + “.ERROR_RESPONSE”, false, stringData);
// Return Error to Mirth to move the file to the error folder
return ERROR;
}
errorCond = “false”;
break;
}
catch(err) {
channelMap.put(“RESPONSE”, err);
responseMap.put(“WEBSVC”, ResponseFactory.getErrorResponse(err))
throw(err);
// Can return ERROR, QUEUED, SENT
// This re-queues the message on a fatal error. I”m doing this since any fatal message may be
// caused by HTTPS connect errors etc. The message will be re-queued
return QUEUED; // Re-queue the message
java.lang.Thread.sleep(6000); // 6 seconds * 10
errorCond = “true”;
}
}
while (errorCond == “true”);
The app I'm integrating returns a 400 bad request when the search doesn't find a match which Zapier sees as an error.
How can I catch the error and return a [] to tell Zapier there is no match?
Here's the existing post_search code of got:
match_person_email_post_search: function(bundle) {
console.log(bundle.response.content);
var res = JSON.parse(bundle.response.content);
var data;
if(res.person!==null)
{
data = [ res.person ];
}
else
{ data = []; }
return data;
},
I should probably mention I'm a rookie at this scripting!
Thanks!
As long as the search API result returns something, you should be able to capture that using Zapier scripting. Assuming your Zapier search key is contact_data, here is how the function would look:
var Zap = {
search_contact_post_search: function(bundle) {
return bundle.response.content;
}
};
So you can check (within that function) if the data is empty or containing something other than search data, and just return an empty array:
var Zap = {
search_contact_post_search: function(bundle) {
var response_content = JSON.parse(bundle.response.content);
if (!response_content.some_field) {
return [];
}
}
};
In Acumatica you can use notifications to automate some emails.
In my scenario, we are creating a process that will at non-specific (non-set) times need to send an email when a specific condition is triggered, such as an employee needs to know they need to do something.
We are building this logic into the system and I am looking for a code sample of how to send the email when this happens.
We will be using an email template, but need to accomplish the feat in code.
I would hope there should be some kind of acumatica email class where we could just call it and pass the required info something like:
PX.Common.email.Send(params)...
Any example code would be appreciated.
It turns out that there is a KB article that gives an example of how to do this.
for our scenario, Here is a more recent version of the code that has been verified to send an email using either of 2 email templates.
private void mSendEmail(string toEmail, int? emailTemplateID, long? noteid, string source, string toDisplayName)
{
bool sent = false;
string sError = "Failed to send E-mail.";
POOrder porec = poOrder.Select(noteid);
EPExpenseClaim eprec = epExpense.Select(noteid);
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.notificationID, Equal<Required<Notification.notificationID>>>>.Select(this, emailTemplateID);
if (rowNotification == null)
throw new PXException(PXMessages.Localize("Notification Template for Escalation is not specified."));
if (String.IsNullOrEmpty(toEmail))
throw new PXException(PXMessages.Localize("E-mail is not specified for Escalation Employee. Name=[" + toDisplayName +"]"));
if (source == "PO")
{
var sender = TemplateNotificationGenerator.Create(porec, rowNotification.NotificationID.Value);
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = toEmail;
IEnumerable<EPActivity> epActivityArray = sender.Send();
if (epActivityArray.Count() > 0)
{ sent = true; }
}
if (source == "EP")
{
var sender = TemplateNotificationGenerator.Create(eprec, rowNotification.NotificationID.Value);
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = toEmail;
IEnumerable<EPActivity> epActivityArray = sender.Send();
if (epActivityArray.Count() > 0)
{ sent = true; }
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(PXMessages.Localize(sError));
}
Here I want to present shorter version of sending email:
using PX.Objects.EP;
using PX.Data.EP;
**...**
var sender = new NotificationGenerator
{
To = "someone#example.com",
Subject = $"Subject information {DateTime.Now:d}",
Body = "Body of message",
BodyFormat = EmailFormatListAttribute.Text
};
sender.Send();
I researched questions on forum but not find true result.
Error:
Metadata query failed for //localhost:5781/odata/$metadata; Unable to process returned
metadata: NamingConvention for this server property name does not roundtrip
properly:diagram_id-->Diagram_id Error: Metadata query failed for //localhost:5781/odata/$metadata; Unable to process returned metadata: NamingConvention for this server property name does not roundtrip properly:diagram_id
Code
(function () {
'use strict';
var serviceId = 'entityManagerFactory';
angular.module('myApp')
.factory(serviceId, ['breeze', emFactory]);
function emFactory(breeze) {
configureBreeze();
var serviceRoot = window.location.protocol + '//' + window.location.host + '/';
var serviceName = serviceRoot + 'odata/';
var factory = {
newManager: newManager,
serviceName: serviceName
};
return factory;
function configureBreeze() {
// use Web API OData to query and save
breeze.config.initializeAdapterInstance('dataService', 'webApiOData', true);
// convert between server-side PascalCase and client-side camelCase
breeze.NamingConvention.camelCase.setAsDefault();
}
function newManager() {
var mgr = new breeze.EntityManager(serviceName);
return mgr;
}
}})();
Code other :
(function () {
'use strict';
var serviceId = 'datacontext';
angular.module('myApp')
.factory(serviceId, ['$q', 'logger', 'entityManagerFactory', datacontext]);
function datacontext($q,logger,emFactory) {
logger = logger.forSource(serviceId);
var logError = logger.logError;
var logSuccess = logger.logSuccess;
var logWarning = logger.logWarning;
var manager = emFactory.newManager();
var service = {
getEmployees: getEmployees
};
return service;
/*Hiện thực ở đây*/
function getChangesCount(){
return manager.getChanges().length;
}
function getEmployees(forceRefresh) {
var count;
if (forceRefresh) {
if(manager.hasChanges()){
count = getChangesCount();
manager.rejectChanges();//undo tất cả các thay đổi ko được lưu
logWarning('Số nhân viên' + count + 'bị thay đổi', null, true);
}
}
// Lúc ko có forceRefesh,xem xét nhận bộ nhớ cache hơn từ xa
return breeze.EntityQuery.from('Employees')
.using(manager).execute()
.then(success).catch(failed);
function success(response) {
count = response.results.length;
logSuccess('Đã nhận ' + count + ' nhân viên', response, true);
return response.results;
}
function failed(error) {
var message = error.message || "Truy vấn để bảng nhân viên bị lỗi";
logError(message, error, true);
}
}
}})();
Code other :
(function () {
'use strict';
var controllerId = 'employees';
angular.module('myApp')
.controller(controllerId, ['datacontext', 'logger', employees]);
function employees(datacontext, logger) {
logger = logger.forSource(controllerId);
var logError = logger.logError;
var logSuccess = logger.logSuccess;
var vm = this;
vm.employees = [];
initialize();
/*Hiện thực*/
function initialize() {
getEmployees();
}
function getEmployees(forceRefresh) {
return datacontext.getEmployees(forceRefresh).then(function (data) {
return vm.employees = data;
console.log(data);
});
}
}}());
This problem very likely has to do with the camelCase naming convention and the language and/or property names that you are using. My guess is that if you remove the line that sets camelCase as the default then the error will go away. If so, then you will need to write your own custom naming convention. See http://www.breezejs.com/documentation/naming-convention
The reason that this is occurring, (I'm guessing here), is that the camelCase naming convention is very simplistic and may not work for your property names and/or language. It assumes that all server property names begin with an uppercase character and that this character can be converted to a lowercase character, and further that this process can be reversed. My guess is that one of your property names already has a first character that is lower case or that calling toLower/toUpper on some first character in a property name does not itself roundtrip. (This can occur is some non-latin character sets).
If either of these cases is occuring, its actually rather easy to create your own namingConvention to use instead of 'camelCase'. Again see the docs mentioned above.