Set field Accessibility to Custom Salesforce Lead field from Java code - salesforce

I am working around with Salesforce and force.com API and metadata API, version 36.
I can create a custom field in a Lead object but by default I can see it's hidden and this means I cannot create a new Lead with these custom fields because it returns a bad request (400 status code).
Is there any way by Code to set the custom field Visible?
public boolean createCustomExtTextField(String name, LoginResult metadataLoginResult, int length) {
boolean success = false;
CustomField cs = new CustomField();
cs.setFullName("Lead."+name+"__c");
cs.setLabel("Custom"+name+"Field");
cs.setType(FieldType.LongTextArea);
cs.setLength(length);
cs.setVisibleLines(50); // max 50
try {
MetadataConnection metadataConnection = createMetadataConnection(metadataLoginResult);
SaveResult[] results = metadataConnection.createMetadata(new Metadata[] { cs });
for (SaveResult r : results) {
if (r.isSuccess()) {
success = true;
} else {
System.out.println("Errors were encountered while creating " + r.getFullName());
for (com.sforce.soap.metadata.Error e : r.getErrors()) {
System.out.println("Error message: " + e.getMessage());
System.out.println("Status code: " + e.getStatusCode());
}
}
}
} catch (ConnectionException e) {
e.printStackTrace();
}
return success;
}
I am googling a lot and don't find something that actually helped. So, any hints are welcomed. Thank you.

Finally found a solution to this. I final one for me was to make all custom fields REQUIRED.
CustomField cs = new CustomField();
cs.setFullName("Lead.YourCompanyName" + name + "__c");
cs.setLabel("YourCompanyName" + name);
cs.setRequired(true);
...
com.sforce.soap.enterprise.LoginResult metadataLoginResult = operations.loginToMetadata(username, password, "https://login.salesforce.com/services/Soap/c/36.0");
...
private boolean createFieldInMetadata(LoginResult metadataLoginResult, CustomField cs) {
boolean success = false;
try {
MetadataConnection metadataConnection = createMetadataConnection(metadataLoginResult);
SaveResult[] results = metadataConnection.createMetadata(new Metadata[] { cs });
for (SaveResult r : results) {
if (r.isSuccess()) {
success = true;
} else {
System.out.println("Errors were encountered while creating " + r.getFullName());
for (com.sforce.soap.metadata.Error e : r.getErrors()) {
System.out.println("Error message: " + e.getMessage());
System.out.println("Status code: " + e.getStatusCode());
}
}
}
} catch (Exception e) {
}
return success;
}
And so it will appear in the page layout. Very important to know, a required field cannot have just an empty value set, it must be something. So if not all custom fields are required in your logic and you wanna avoid the entire process of unzipping page layout and zipping it back (however it may be done) just add "N/A" or any char at choice to the required by code but not your project custom fields.
I managed to make the custom Field Level Security visible for "Admin" profile but not Field Accessability to visible. The latter is untested.

Related

Getting 400 from samltest.id when attempting SP-initiated worflow

I have used itfoxtec's SAML2 library to implement an SP in my ASP.NET MVC app. I am testing using samltest.id as the IdP. The IdP-initiated workflow works perfectly, but the SP-initiated workflow always gets a 400 error back from samltest.id. I have attempted to look through samltest.id's log to see if an error is being recorded there for my request, but I cannot seem to find anything there.
This is the Action that handles the URL that he user would go to when initiating SSO:
public ActionResult SSOLogin() {
LogManager logger = new LogManager("SSOLogin");
string hostname = this.GetHostname();
SchoolSettings settings = this.GetClientSettings();
if (settings.UseSAMLSSO) {
Saml2Configuration samlConfig = null;
try {
samlConfig = SamlConfigLoader.GetSaml2Config(HttpContext, settings, this.IsSandbox());
} catch (Exception e) {
logger.exception($"loading Saml2Configuration for {hostname}", e);
}
if (samlConfig != null) {
try {
var binding = new Saml2RedirectBinding();
binding.SetRelayStateQuery(new Dictionary<string, string> { { "Home/Index", Url.Content("~/") } });
return binding.Bind(new Saml2AuthnRequest(samlConfig) {
}).ToActionResult();
} catch (Exception e) {
logger.error($"Exception redirecting to IdP. {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
ViewBag.ssoerror = $"Error redirecting to IdP for {hostname}";
}
} else {
logger.critical($"Could not load SAML2 configuration for {hostname}");
ViewBag.ssoerror = $"Could not load SAML2 configuration for {hostname}";
}
} else {
ViewBag.ssoerror = "SSO is not configured for this client. Please contact Support";
}
return Redirect("/Home/SSOError");
}
The method that loads a client-specific metadata looks like this:
public static Saml2Configuration GetSaml2Config(HttpContextBase context, SchoolSettings forSchool, bool forSandbox) {
LogManager log = new LogManager("getSaml2Config");
Saml2Configuration config = new Saml2Configuration();
if (!forSandbox) {
config.Issuer = _saml2Issuer;
} else {
config.Issuer = _saml2IssuerSandbox;
}
config.SignatureAlgorithm = _saml2SignatureAlgo;
config.CertificateValidationMode = X509CertificateValidationMode.None;
config.RevocationMode = (X509RevocationMode)Enum.Parse(typeof(X509RevocationMode), ConfigurationManager.AppSettings["Saml2:RevocationMode"]);
config.AllowedAudienceUris.Add(config.Issuer);
var entityDescriptor = new EntityDescriptor();
if (forSchool.SAMLMetadataLocationIsUrl) {
try {
entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(forSchool.SAMLMetadataLocation));
} catch (Exception e) {
log.error($"Exception caught loading metadata from school {forSchool.Hostname} at URL {forSchool.SAMLMetadataLocation}\n Exception {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
entityDescriptor.IdPSsoDescriptor = null;
}
} else {
var schoolMetadataPath = context.Server.MapPath("~/App_Data/SAMLMetadata/" + forSchool.SAMLMetadataLocation);
log.info($"Loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}");
try {
entityDescriptor.ReadIdPSsoDescriptorFromFile(schoolMetadataPath);
} catch (IOException ioe) {
log.error($"IOException caught loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}: {ioe.Message}\n{ioe.StackTrace}");
entityDescriptor.IdPSsoDescriptor = null;
} catch (Exception e) {
log.error($"Exception caught loading metadata for school {forSchool.Hostname} from file {schoolMetadataPath}\n Exception {e.GetType().ToString()}: {e.Message}\n{e.StackTrace}");
entityDescriptor.IdPSsoDescriptor = null;
}
}
if (entityDescriptor.IdPSsoDescriptor != null) {
if (entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.Count() > 0) {
config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
} else {
log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SingleSignOnServices that could be parsed.");
}
if (entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.Count() > 0) {
config.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location;
} else {
log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SingleLogoutServices that could be parsed.");
}
if (entityDescriptor.IdPSsoDescriptor.SigningCertificates.Count() > 0) {
config.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates);
} else {
log.error($"WARNING: metadata for {forSchool.Hostname} does not have any SigningCertificates that could be parsed.");
}
} else {
throw new Exception("IdPSsoDescriptor not loaded from metadata.");
}
return config;
}
If it would help to clarify the situation, I can add the code for the AssertionConsumerService Action which works perfectly in an IdP-initiated scenario.
I discovered the problem. It comes down to this line of code in the GetSaml2Config method:
config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location;
This naively takes the first SingleSignOnService element in the metadata and decides that it is the correct one to use, but that was not always the case that this assumption was true. What I really wanted was to get a SingleSignOnService element for and HTTP-POST binding:
config.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.Where(s => s.Binding.ToString().IndexOf("HTTP-POST") > 0).FirstOrDefault()?.Location;
This works well for all of the cases that I have found since.
Your code looks correct.
It is probably an integration issue but very hard to find if the IdP do not log an error message.
What error status message do you get back instead of success, maybe that tells you something.
Maybe the IdP do not accept the SAML 2.0 Authn Response, here is something to look for:
The config.SingleSignOnDestination probably is required
Meybe the IdP requere the request to be signed
It is also possible to add other attributes in the request, do the IdP documentation describe any requirements?

Get Unread emails from Google API

I'm trying to get the count of unread email using google API, but not able. ANy help is highly appreciated. I'm not getting any error, but the count doesnt match the actual number shown in gmail.
try
{
String serviceAccountEmail = "xxx#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"C:\Projects\xxx\xyz\API Project-xxxxx.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = "xxx#gmail.com",
Scopes = new[] { Google.Apis.Gmail.v1.GmailService.Scope.GmailReadonly }
}.FromCertificate(certificate));
var gmailservice = new Google.Apis.Gmail.v1.GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GoogleApi3",
});
try
{
List<Message> lst = ListMessages(gmailservice, "xxx#gmail.com", "IN:INBOX IS:UNREAD");
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
}
catch (Exception ex)
{
}
Just do: labels.get(id="INBOX") and it has those types of stats (how many messages in that label, how many are unread, and same for threads).
https://developers.google.com/gmail/api/v1/reference/users/labels/get
You can use the ListMessages method from the API example (included for completeness) for searching:
private static List<Message> ListMessages(GmailService service, String userId, String query)
{
List<Message> result = new List<Message>();
UsersResource.MessagesResource.ListRequest request = service.Users.Messages.List(userId);
request.Q = query;
do
{
try
{
ListMessagesResponse response = request.Execute();
result.AddRange(response.Messages);
request.PageToken = response.NextPageToken;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
} while (!String.IsNullOrEmpty(request.PageToken));
return result;
}
You can use this search method to find unread messages, for example like this:
List<Message> unreadMessageIDs = ListMessages(service, "me", "is:unread");
The q parameter (query) can be all kinds of stuff (it is the same as the gmail search bar on the top of the web interface), as documented here: https://support.google.com/mail/answer/7190?hl=en.
Note that you only a few parameters of the Message objects are set. If you want to retreive the messages you'll have to use GetMessage method from the api:
public static Message GetMessage(GmailService service, String userId, String messageId)
{
try
{
return service.Users.Messages.Get(userId, messageId).Execute();
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
return null;
}
I agree that the API is not straight forward and misses a lot of functionality.
Solution for .Net:
// Get UNREAD messages
public void getUnreadEmails(GmailService service)
{
UsersResource.MessagesResource.ListRequest Req_messages = service.Users.Messages.List("me");
// Filter by labels
Req_messages.LabelIds = new List<String>() { "INBOX", "UNREAD" };
// Get message list
IList<Message> messages = Req_messages.Execute().Messages;
if ((messages != null) && (messages.Count > 0))
{
foreach (Message List_msg in messages)
{
// Get message content
UsersResource.MessagesResource.GetRequest MsgReq = service.Users.Messages.Get("me", List_msg.Id);
Message msg = MsgReq.Execute();
Console.WriteLine(msg.Snippet);
Console.WriteLine("----------------------");
}
}
Console.Read();
}

The given key was not present in the dictionary solrnet

Please note: I know for the question SolrNet - The given key was not present in the dictionary and I have initialized solr object just like Mauricio suggests.
I am using solr 4.6.0 and solrnet build #173, .net framework 4.0 and VS2012 for development. For some unknown reason I am receiving error 'The given key was not present in the dictionary'. I have a document with that id in solr, I've checked via browser. It's a document like any other document. Why is error popping up? My code (I've made a comment on the place where the error happens):
//establishes connection with solr
private void ConnectToSolr()
{
try
{
if (_solr != null) return;
Startup.Init<Register>(SolrAddress);
_solr = ServiceLocator.Current.GetInstance<ISolrOperations<Register>>();
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
//Returns snippets from solr as BindingSource
public BindingSource GetSnippets(string searchTerm, DateTime? startDate = null, DateTime? endDate = null)
{
ConnectToSolr();
string dateQuery = startDate == null
? ""
: endDate == null
? "savedate:\"" + convertDateToSolrFormat(startDate) + "\"" //only start date
: "savedate:[" + convertDateToSolrFormat(startDate) + " TO " +
convertDateToSolrFormat(endDate) + "]";//range between start and end date
string textQuery = string.IsNullOrEmpty(searchTerm) ? "text:*" : "text:*" + searchTerm + "*";
List<Register> list = new List<Register>();
SolrQueryResults<Register> results;
string currentId = "";
try
{
results = _solr.Query(textQuery,
new QueryOptions
{
Highlight = new HighlightingParameters
{
Fields = new[] { "*" },
},
ExtraParams = new Dictionary<string, string>
{
{"fq", dateQuery},
{"sort", "savedate desc"}
}
});
for (int i = 0; i < results.Highlights.Count; i++)
{
currentId = results[i].Id;
var h = results.Highlights[currentId];
if (h.Snippets.Count > 0)
{
list.Add(new Register//here the error "the given key was not present in the dictionary pops up
{
Id = currentId,
ContentLiteral = h.Snippets["content"].ToArray()[0].Trim(new[]{' ', '\n'}),
SaveDateLiteral = results[i].SaveDate.ToShortDateString()
});
}
}
BindingList<Register> bindingList = new BindingList<Register>(list);
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = bindingList;
return bindingSource;
}
catch(Exception e)
{
MessageBox.Show(string.Format("{0}\nId:{1}", e.Message, currentId), "Solr error");
return null;
}
}
I've found out what's causing the problem: saving empty documents into solr. If I make an empty query (with text:*) through solrnet (usually I do this if I want to see all saved documents) and empty document is one of saved docs, then 'The given key is not present in dictionary pops up'. If all of the documents have text in them, this error doesn't pop up.
If you document contains fields with types other than string and you index null value to a double or integer field you will get the same error.
solr query return the null field as:
<null name="fieldname"/>
should be
<double name="fieldname">0.0</double>
or
<double name="fieldname"/>

OptionSetValue does not contain constructor which takes one argument

I am creating a silverlight application as a web resource for CRM 2011. Now i am creating a ServiceAppointment record in DB and after creating it i want to change its Status to "reserved" instead of requested.
I googled about this and come across the examples like Close a Service Activity Through Code and Microsoft.Crm.Sdk.Messages.SetStateRequest
They all suggesting to use "SetStateRequest" and for using this i have to set the OptionSetValue like
request["State"] = new OptionSetValue(4);
But above line gives me error saying "OptionSetValue does not contain constructor which takes one argument"
BTW i am using SOAP end point of CRM 2011 service in silverlight application
Any ideas friends?
EDIT
Following is my code
var request = new OrganizationRequest { RequestName = "SetStateRequest" };
request["State"] = 3;
request["Status"] = 4;
request["EntityMoniker"] = new EntityReference() { Id = createdActivityId, LogicalName = "serviceappointment" };
crmService.BeginExecute(request,ChangeActivityStatusCallback,crmService);
And my callback function is
private void ChangeActivityStatusCallback(IAsyncResult result) {
OrganizationResponse response;
try
{
response = ((IOrganizationService)result.AsyncState).EndExecute(result);
}
catch (Exception ex)
{
_syncContext.Send(ShowError, ex);
return;
}
}
You must some how be referencing some other OptionSetValue class that is not the Microsoft.Xrm.Sdk one. Try appending the namespace to see if that resolves your issue:
request["State"] = new Microsoft.Xrm.Sdk.OptionSetValue(4);
Also, why are you using late bound on the SetStateRequest? Just use the SetStateRequest class:
public static Microsoft.Crm.Sdk.Messages.SetStateResponse SetState(this IOrganizationService service,
Entity entity, int state, int? status)
{
var setStateReq = new Microsoft.Crm.Sdk.Messages.SetStateRequest();
setStateReq.EntityMoniker = entity.ToEntityReference();
setStateReq.State = new OptionSetValue(state);
setStateReq.Status = new OptionSetValue(status ?? -1);
return (Microsoft.Crm.Sdk.Messages.SetStateResponse)service.Execute(setStateReq);
}
Thanks Daryl for you time and effort. I have solved my problem with the way u have suggested.
I am posting my code that worked for me.
var request = new OrganizationRequest { RequestName = "SetState" };
request["State"] = new OptionSetValue { Value = 3 };
request["Status"] = new OptionSetValue { Value = 4 };
request["EntityMoniker"] = new EntityReference() { Id = createdActivityId, LogicalName = "serviceappointment" };
crmService.BeginExecute(request,ChangeActivityStatusCallback,crmService);
private void ChangeActivityStatusCallback(IAsyncResult result) {
OrganizationResponse response;
try
{
response = ((IOrganizationService)result.AsyncState).EndExecute(result);
}
catch (Exception ex)
{
_syncContext.Send(ShowError, ex);
return;
}
}

what is wrong with my test class in apex force.com code?

I have written a working class in Apex. It is an Email service extender, that processes incoming emails.
It is working perfect on my sandbox enviroment.
I have created a test class, so I can also deploy it to my production, but when validating the code, I get the only 72% of my code is tested.
This is my main class
global class inboundEmail implements Messaging.InboundEmailHandler {
global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
Lead lead;
String [] mFromUserParams;
String [] sourceText;
String mCaseObject;
try{
sourceText = email.toAddresses[0].split('#');
String [] mParams = sourceText[0].split('\\.');
**// FROM THIS LINE TO THE END - NOT COVERED**
mFromUserParams = email.fromAddress.split('#');
mCaseObject = mParams[0];
if (mCaseObject == 'lead'){
lead = new Lead();
lead.LastName = mFromUserParams[0];
lead.Company = email.fromAddress;
lead.OwnerId = mParams[1];
lead.LeadSource = mParams[2];
lead.Email = email.fromAddress;
lead.RequirementsDescription__c = email.subject + email.plainTextBody;
insert lead;
result.success = true;
} else if (mCaseObject == 'case'){
result.success = true;
} else {
result.success = false;
}
}catch(Exception e){
result.success = false;
result.message = 'Oops, I failed.';
}
return result;
}
}
This is my test class
#isTest
private class inboundEmailTest {
public static testMethod void inboundEmail(){
// Create a new email, envelope object and Header
Messaging.InboundEmail email = new Messaging.InboundEmail();
Messaging.InboundEnvelope envelope = new Messaging.InboundEnvelope();
envelope.toAddress = 'lead.owner.new#cpeneac.cl.apex.sandbox.salesforce.com';
envelope.fromAddress = 'user#acme.com';
email.subject = 'Please contact me';
email.fromName = 'Test From Name';
email.plainTextBody = 'Hello, this a test email body. for testing Bye';
// setup controller object
inboundEmail catcher = new inboundEmail();
Messaging.InboundEmailResult result = catcher.handleInboundEmail(email, envelope);
}
}
According to the error message, ALL lines in the Try/Catch block from the 3rd line are not covered. (marked in the code).
in your test method you're setting envelope.toAddress but in your email service you're splitting the first element of the actual InboundEmail objects toAddresses. this probably causes either an ArrayIndexOutOfBoundsException or a NPE because the element 0 does not exist. so the code coverage will be poor because your test always jumps into the exception handling and leaves the rest of you code uncovered. just set the emails toAddresses and you should have a better coverage.
h9nry
In your test code, can you add a scenario that causes the lead insert to fail? This will cause the code in your catch block to execute and provide you the needed code test coverage.
The email.fromAddress is not a list by default, so just setting that to a string and not a list solved this.

Resources