I'm trying to send a HttpPOST with Content-Type multipart/form-data using Apex, the API response is 'invalid credentials', the problem is not in the data because it works on Postman, I can't find where is the problem in my code:
My class:
public with sharing class XXXXXXXXXXXX {
#future(callout = true)
public static void fileUpload(){
String endpoint = 'endpoint';
List<ContentVersion> cvList = new List<ContentVersion>();
cvList = [SELECT id, Title, ContentDocumentId, FileExtension, VersionData FROM ContentVersion WHERE Id = '0685e000004zxRrAAI' LIMIT 1];
System.debug('Callout Log 1: ' + cvList);
if(!cvList.isEmpty()){
//form fileName with cv ID
String fileName = cvList[0].Id;
if(cvList[0].FileExtension != null && cvList[0].FileExtension != ''){
fileName = fileName + '.' + cvList[0].FileExtension;
}
System.debug('Callout log2: ' + fileName);
//callout ePOR service
String contentType = EinsteinVision_HttpBodyPart.GetContentType();
//Compose the form
string form64 = '';
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('message', 'message value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('login', 'login value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('password', 'password value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('to', 'to value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBlobBodyParameter('image', EncodingUtil.base64Encode(cvList[0].VersionData), fileName);
Blob formBlob = EncodingUtil.base64Decode(form64);
String contentLength = String.valueOf(formBlob.size());
System.debug('Callout Log 3: ' + formBlob.size());
if(formBlob.size() > 12000000){
System.debug('Callout error');
}else{
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Length', contentLength);
req.setHeader('Content-Type', contentType);
req.setTimeout(120000);
HttpResponse response = http.send(req);
String responseBody = response.getBody();
System.debug('Callout Log 4: ' + response.getStatusCode());
System.debug('Callout Log 5: ' + String.valueOf(response.getBody()));
}
}
}
}
Class to handle Content-Type multipart/form-data:
public virtual class EinsteinVision_HttpBodyPart {
public EinsteinVision_HttpBodyPart() {
}
// The boundary is aligned so it doesn't produce padding characters when base64 encoded.
private final static string Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';
public static String getBoundary() {
return Boundary;
}
/**
* Returns the request's content type for multipart/form-data requests.
*/
public static string GetContentType() {
return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
}
/**
* Pad the value with spaces until the base64 encoding is no longer padded.
*/
public static string SafelyPad(
string value,
string valueCrLf64,
string lineBreaks) {
string valueCrLf = '';
blob valueCrLfBlob = null;
while (valueCrLf64.endsWith('=')) {
value += ' ';
valueCrLf = value + lineBreaks;
valueCrLfBlob = blob.valueOf(valueCrLf);
valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
}
return valueCrLf64;
}
/**
* Write a boundary between parameters to the form's body.
*/
public static string WriteBoundary() {
string value = '--' + Boundary + '\r\n';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a boundary at the end of the form's body.
*/
public static string WriteBoundary(
EndingType ending) {
string value = '';
if (ending == EndingType.Cr) {
// The file's base64 was padded with a single '=',
// so it was replaced with '\r'. Now we have to
// prepend the boundary with '\n' to complete
// the line break.
value += '\n';
} else if (ending == EndingType.None) {
// The file's base64 was not padded at all,
// so we have to prepend the boundary with
// '\r\n' to create the line break.
value += '\r\n';
}
// Else:
// The file's base64 was padded with a double '=',
// so they were replaced with '\r\n'. We don't have to
// do anything to the boundary because there's a complete
// line break before it.
value += '--' + Boundary + '--';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a key-value pair to the form's body.
*/
public static string WriteBodyParameter(
string key,
string value) {
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"';
string contentDispositionCrLf = contentDisposition + '\r\n\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n\r\n');
string valueCrLf = value + '\r\n';
blob valueCrLfBlob = blob.valueOf(valueCrLf);
string valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
content += SafelyPad(value, valueCrLf64, '\r\n');
return content;
}
/**
* Write a key-value pair to the form's body for a blob.
*/
public static string WriteBlobBodyParameter(string key, string file64, string fileName) {
String mimeType = resolveMimeType(fileName);
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"; filename="'+fileName+'"';
string contentDispositionCrLf = contentDisposition + '\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n');
string contentTypeHeader = 'Content-Type: ' + mimeType;
string contentTypeCrLf = contentTypeHeader + '\r\n\r\n';
blob contentTypeCrLfBlob = blob.valueOf(contentTypeCrLf);
string contentTypeCrLf64 = EncodingUtil.base64Encode(contentTypeCrLfBlob);
content += SafelyPad(contentTypeHeader, contentTypeCrLf64, '\r\n\r\n');
integer file64Length = file64.length();
String last4Bytes = file64.substring(file64.length()-4,file64.length());
// Avoid padding the file data with spaces, which SafelyPad does
// http://salesforce.stackexchange.com/a/33326/102
EndingType ending = EndingType.None;
if (last4Bytes.endsWith('==')) {
// The '==' sequence indicates that the last group contained only one 8 bit byte
// 8 digit binary representation of CR is 00001101
// 8 digit binary representation of LF is 00001010
// Stitch them together and then from the right split them into 6 bit chunks
// 0000110100001010 becomes 0000 110100 001010
// Note the first 4 bits 0000 are identical to the padding used to encode the
// second original 6 bit chunk, this is handy it means we can hard code the response in
// The decimal values of 110100 001010 are 52 10
// The base64 mapping values of 52 10 are 0 K
// See http://en.wikipedia.org/wiki/Base64 for base64 mapping table
// Therefore, we replace == with 0K
// Note: if using \n\n instead of \r\n replace == with 'oK'
last4Bytes = last4Bytes.substring(0,2) + '0K';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the \r\n to the Blob, so leave footer as it is.
ending = EndingType.CrLf;
} else if (last4Bytes.endsWith('=')) {
// '=' indicates that encoded data already contained two out of 3x 8 bit bytes
// We replace final 8 bit byte with a CR e.g. \r
// 8 digit binary representation of CR is 00001101
// Ignore the first 2 bits of 00 001101 they have already been used up as padding
// for the existing data.
// The Decimal value of 001101 is 13
// The base64 value of 13 is N
// Therefore, we replace = with N
last4Bytes = last4Bytes.substring(0,3) + 'N';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the CR e.g. \r, still need to prepend the line feed to the footer
ending = EndingType.Cr;
}
content += file64;
content += WriteBoundary(ending);
return content;
}
private static String resolveMimeType(String fileName) {
String fileType = fileName.substringAfterLast('.');
String mimeType = 'image/png'; // fallback value
if (fileType.equalsIgnoreCase('png')) {
mimeType = 'image/png';
} else if (fileType.equalsIgnoreCase('jpeg') || fileType.equalsIgnoreCase('jpg')) {
mimeType = 'image/jpg';
} else if (fileType.equalsIgnoreCase('pgm')) {
mimeType = 'image/x-portable-graymap';
} else if (fileType.equalsIgnoreCase('ppm')) {
mimeType = 'image/x-portable-pixmap';
}
return mimeType;
}
/**
* Helper enum indicating how a file's base64 padding was replaced.
*/
public enum EndingType {
Cr,
CrLf,
None
}
}
The second class is not mine and it should be good, the problem is on the first class [XXXXXXXXXXXX]
Thanks!
Related
I have an encryption class in Dart, which will encrypt User ID for posting it to MariaDB table with REST (Post Request, ? and * is placeholder, these are different in my real code.):
import 'package:encrypt/encrypt.dart';
//import 'dart:convert' as convert;
class Security {
///Key
static final _key =
Key.fromUtf8('********************************'); //32 chars
static final iv = IV.fromUtf8('????????????????'); //16 chars
///encrypt, [text] is data.
static String encryptMyData(String text) {
final e = Encrypter(AES(_key, mode: AESMode.cbc));
final encryptedData = e.encrypt(text, iv: iv);
return encryptedData.base64;
}
///decrypt, [text] is data.
static String decryptMyData(String text) {
final e = Encrypter(AES(_key, mode: AESMode.cbc));
final decryptedData = e.decrypt(Encrypted.fromBase64(text), iv: iv);
return decryptedData;
}
}
Sometimes, "/" char coming in encrypted ID, which causes 404 and 405 errors on POST request. Because "/" is HTML routing character.
My POST code is:
///Adds an user item to database via decoding / encoding the JSON value.
///[name] is name, [surname] is surname, [uid] is user id, [type] is user role, [debt] is user debt, [paucode] is pay code.
static Future<bool> addUserToDatabase(String name, String surname, String uid,
double debt, String paycode, int type) async {
try {
if (name != null &&
surname != null &&
uid != null &&
debt != null &&
paycode != null &&
type != null) {
var addingUser = User(name.toLowerCase(), surname.toLowerCase(),
Security.encryptMyData(uid), type, debt, paycode);
print('This user will be added:\n------\n$addingUser\n-----------\n');
var uri = Uri.https('localhost:5001',
'User/username=${addingUser.userName}&surname=${addingUser.userSurname}&id=${addingUser.personID}&debt=${addingUser.userDebt}&type=${addingUser.type}&paycode=${addingUser.payCode}');
print('The URI: $uri');
var request = await http.post(uri);
print(
'The response:\n----\n- ${request.body}\n Status Code: ${request.statusCode}\n----\n');
if (request.statusCode == 201 || request.statusCode == 200) {
// If the server did return a 201 Created response,
// then return value.
return true;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception(
'Failed to add user. Status Code: ${request.statusCode}');
}
}
return false;
} catch (e) {
print('An error occurred - $e');
return false;
}
}
How I escaping from "/" character in Dart encryption ? I don't want this character in the encrypted string. Thanks.
encryptMyData() Base64 encodes the data. The Base64 alphabet contains alphanumeric characters and the characters /, + and = (padding), where the last three have a special meaning and are therefore reserved in the context of URIs.
A simple way to mask these characters would be to URL encode the Base64 encoded strings with Uri.encodeComponent(). Accordingly, URL decoding with Uri.decodeComponent() must be performed before Base64 decoding:
var base64Data = 'EBES/+7dzA==';
var urlEnc = Uri.encodeComponent(base64Data);
print(urlEnc); // EBES%2F%2B7dzA%3D%3D
// Apply in URL
var urlDec = Uri.decodeComponent(urlEnc);
print(urlDec); // EBES/+7dzA==
Alternatively, Base64url can be applied instead of Base64. Base64url uses the characters _ and - instead of / and + as well as optional padding. Unlike / and +, _ and - are not reserved for URIs.
Base64url is supported in Dart by base64Url.encode(). The padding is applied, but can be removed explicitly (e.g. with replaceAll('=','')). Decoding is possible with base64Url.decode() (after the padding has been added). Alternatively, base64Url.normalize() can be used to convert to Base64 (which automatically adds the padding) and base64.decode() can be applied for Base64 decoding:
var b64urlEnc = base64Url.encode([16, 17, 18, 255, 238, 221, 204]);// [0x10, 0x11, 0x12, 0xff, 0xee, 0xdd, 0xcc]
print(b64urlEnc); // EBES_-7dzA==
var b64urlEncNoPadding = b64urlEnc.replaceAll('=','');
print(b64urlEncNoPadding); // EBES_-7dzA
// Apply in URL
var base64Enc = base64Url.normalize(b64urlEncNoPadding);
print(base64Enc); // EBES/+7dzA==
var base64Dec = base64.decode(base64Enc);
print(base64Dec); // [16, 17, 18, 255, 238, 221, 204]
Optionally, for already Base64 encoded data, the characters / and + can be replaced by _ and - and the padding (=) can be removed.
Say I have a string:
typed = "need replace this ap"
str = "hello I need to replace this asap"
so the end result I want would be this:
newStr = "hello I <bold>need</bold> to <bold>replace</bold> <bold>this</bold> as<bold>ap</bold>"
please don't mind the weird syntax.
I wonder if the order would matter, for example:
typed = "applicable app"
str = "the app is very applicable in many applications"
The end result I wish should be:
newStr = "the <bold>app</bold> is very <bold>applicable</bold> in many <bold>app</bold>lications"
right? is this possible?
Hey,If You can ignore the weird HTML syntax here,
Then I have wrote a solution for you,
Paste this code in dart pad here
removeDuplicates(var typed, var str) {
Map<String, String> m = new Map<String, String>();
var n = typed.length;
String ans = "";
//for storing the "typed" string (word by word) into a map "m" variable for later searching purpose
String temp = "";
int i = 0;
for (i = 0; i < n; i++) {
if (typed[i] == " ") {
m[temp] = temp;
temp = "";
} else {
temp = temp + typed[i];
}
}
//for storing the last word of the string "typed", coz loop will never find a space in last of the string
m[temp] = temp;
// map variable loop for search from map "m" in the "str" string, and matching if the word is present or not
var n2 = str.length;
String temp2 = "";
for (int j = 0; j < n2; j++) {
if (str[j] == " ") {
if (m.containsKey(temp2)) {
} else {
ans = ans + " " + temp2; //storing the "temp2" string into "ans" string, everytime it finds a space and if the string is not already present in the map "m"
}
temp2 = "";
} else {
temp2 = temp2 + str[j];
}
}
//for searching for the last word of the string "str" in map "m", coz loop will never find a space in last of the string,
if (m.containsKey(temp2)) {
} else {
ans = ans + " " + temp2;
}
return ans;
}
void main() {
String typed = "need replace this ap";
var str = "hello I need to replace this asap";
String answer = removeDuplicates(typed, str);
print(answer);
}
Here, I have made a method removeDuplicates() to simplify your work, You just have to pass those string in your method, and then it will return you the desired answer string by removing the duplicates, with a new string.
UPDATED CODE (TO SHOW HTML CODE):
removeDuplicates(var typed, var str) {
Map<String, String> m = new Map<String, String>();
var n = typed.length;
String ans = "";
//for storing the "typed" string (word by word) into a map "m" variable for later searching purpose
String temp = "";
int i = 0;
for (i = 0; i < n; i++) {
if (typed[i] == " ") {
m[temp] = temp;
temp = "";
} else {
temp = temp + typed[i];
}
}
//for storing the last word of the string "typed", coz loop will never find a space in last of the string
m[temp] = temp;
print(m);
// map variable loop for search from map "m" in the "str" string, and matching if the word is present or not
var n2 = str.length;
String temp2 = "";
for (int j = 0; j < n2; j++) {
if (str[j] == " ") {
if (m.containsKey(temp2)) {
temp2 = "<bold>" + temp2 + "</bold> ";
ans = ans + " " + temp2;
} else {
ans = ans +
" " +
temp2; //storing the "temp2" string into "ans" string, everytime it finds a space and if the string is not already present in the map "m"
}
temp2 = "";
} else {
temp2 = temp2 + str[j];
}
}
//for searching for the last word of the string "str" in map "m", coz loop will never find a space in last of the string,
if (m.containsKey(temp2)) {
temp2 = "<bold>" + temp2 + "</bold> ";
temp2 = "";
} else {
ans = ans + " " + temp2;
temp2 = "";
}
return ans;
}
void main() {
var typed = "applicable app";
var str = "the app is very applicable in many applications";
String answer = removeDuplicates(typed, str);
print(answer);
}
UPDATE 2 (ALL THANKS TO PSKINK FOR THE str.replaceAllMapped APPROACH)
replaceWithBoldIfExists(String typed, String str) {
var n = typed.length;
List<String> searchList = new List<String>();
String temp = "";
int i = 0;
for (i = 0; i < n; i++) {
if (typed[i] == " ") {
searchList.add(temp);
temp = "";
} else {
temp = temp + typed[i];
}
}
searchList.add(temp);
String pat = searchList.join('|');
final pattern = RegExp(pat);
final replaced =
str.replaceAllMapped(pattern, (m) => '<bold>${m.group(0)}</bold>');
return replaced;
}
void main() {
var typed = "need replace this ap";
var str = "hello I need to replace this asap";
print(replaceWithBoldIfExists(typed, str));
}
I need to convert a string of 8-character hexadecimal substrings into a list of integers.
For example, I might have the string
001479B70054DB6E001475B3
which consists of the following substrings
001479B7 // 1341879 decimal
0054DB6E // 5561198 decimal
001475B3 // 1340851 decimal
I'm currently using convert.hex to first convert the strings into a list of 4 integers (because convert.hex only handles parsing 2-character hex strings) and then adding/multiplying those up:
String tmp;
for(int i=0; i<=myHexString.length-8; i+=8){
tmp = myHexString.substring(i, i+8);
List<int> ints = hex.decode(tmp);
int dec = ints[3]+(ints[2]*256+(ints[1]*65536)+(ints[0]*16777216));
}
Is there a more efficient way to do this?
You can use int.parse('001479B7', radix: 16);
https://api.dartlang.org/stable/2.4.1/dart-core/int/parse.html
so your code will look like this :
void main() {
final fullString = '001479B70054DB6E001475B3';
for (int i = 0; i <= fullString.length - 8; i += 8) {
final hex = fullString.substring(i, i + 8);
final number = int.parse(hex, radix: 16);
print(number);
}
}
Since my Hex string came smaller than 8 elements of Byte, I did this.
String dumpHexToString(List<int> data) {
StringBuffer sb = StringBuffer();
data.forEach((f) {
sb.write(f.toRadixString(16).padLeft(2, '0'));
sb.write(" ");
});
return sb.toString();
}
String conertHexDecimal(String str1) {
final fullString = str1;
int number = 0;
for (int i = 0; i <= fullString.length - 8; i += 8) {
final hex = fullString.substring(i, i + 8);
number = int.parse(hex, radix: 16);
print(number);
}
return number.toString();
}
void executarConersao(Uint8List data){
String conersorHexDeVar = dumpHexToString(data);
conersorHexDeVar = conersorHexDeVar
.substring(3, conersorHexDeVar.length)
.replaceAll(' ', '')
.padLeft(8, '0');
conersorHexDeVar = conertHexDecimal(conersorHexDeVar);
print('data $conersorHexDeVar');
}
For anyone who wants to convert hexadecimal numbers to 2's component, Dart / Flutter has a builtin method - .toSigned(int):
var testConversion = 0xC1.toSigned(8);
print("This is the result: " + testConversion.toString()); // prints -63
I am using this code but gives me this error:
Failed to retrieve data from the database. Details: [Database Vendor Code: 201 ]
public JsonResult ReportCertificateOfEmployment(int EmployeeId, int SigId, string SigPosition)
{
string Userkey = "gHeOai6bFzWskyUxX2ivq4+pJ7ALwbzwF55dZvy/23BrHAfvDVj7mg ";
string PassKey = "lLAHwegN8zdS7mIZyZZj+EmzlkUXkvEYxLvgAYjuBVtU8sw6wKXy2g ";
string rptlogin = ConfigurationManager.AppSettings.Get("rptlogin");
string rptPassword = ConfigurationManager.AppSettings.Get("rptPassword");
MemoryStream oStream;
CertificateOfEmployment rpt = new CertificateOfEmployment();
rpt.Refresh();
rpt.SetDatabaseLogon(rptlogin, rptPassword);
rpt.SetParameterValue(0, EmployeeId);
rpt.SetParameterValue(1, SigId);
rpt.SetParameterValue(2, SigPosition);
rpt.SetParameterValue(3, DateTime.Now);
oStream = (MemoryStream)rpt.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat); --------- this gives me an error
// extract only the fielname
string filename = Convert.ToString((DateTime.Now.Month) + Convert.ToString(DateTime.Now.Day) + Convert.ToString(DateTime.Now.Year) + Convert.ToString(DateTime.Now.Hour) + Convert.ToString(DateTime.Now.Minute) + Convert.ToString(DateTime.Now.Second) + Convert.ToString(DateTime.Now.Millisecond)) + "BarangayClearance";
var len = oStream.Length;
// store the file inside ~/App_Data/uploads folder
// var path = Path.Combine(Server.MapPath("~/Content/Images"), fileName);
// file.SaveAs(path);
FileTransferServiceClient client2 = new FileTransferServiceClient();
RemoteFileInfo rmi = new RemoteFileInfo();
DateTime dt = DateTime.Now;
// upload file using webservice
DownloadRequest dr = new DownloadRequest();
//web service method to upload a file and return the unique id of the newly uploaded file
string fId = client2.UploadFileGetId("", filename, len, PassKey, Userkey, oStream);
//Instantiate the object Img which is a table in the database server of the application
//Download file using web service;
//DownloadFile in Refence.cs has been edited to return a RemoteFileInfo Class
//before, the return type of DownloadFile method was a Stream type
JsonResult result = new JsonResult();
result.Data = new
{
fileId = fId,
filename = filename
};
rpt.Close();
rpt.Dispose();
var arr = oStream.ToArray();
oStream.Close();
oStream.Dispose();
result.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return result;
}
I'm trying to save a PDF from QBO however I'm stuck on this bit:
How do i get the IConsumerRequest to return a stream instead of a string? ReadBody only seems to send string rather than binary data...
IConsumerRequest conReq = oSession.Request();
conReq = conReq.Get().WithRawContentType("application/pdf");
string outURL = base_url + "invoice-document/v2/" + realmId + "/" + customerInvoicesWithinDateRange[0].Id.Value;
conReq = conReq.ForUrl(outURL);
conReq = conReq.SignWithToken();
string serviceResponse = conReq.ReadBody();
Thanks
instead of conReeq.ReadBody(), you can do this:
conReq.ToWebResponse().GetResponseStream();
in fact, ReadBody() is simply an extension method on IConsumerRequest, defined as:
public static string ReadBody(this IConsumerRequest request)
{
HttpWebResponse response = request.ToWebResponse();
return response.ReadToEnd();
}