Re-index items in DSpace 6.2 after updating through REST - solr

We are trying to build an application to provide bulk editing of item metadata ingested in DSpace, using the REST API. The update operations are being reflected in the DSpace UI. However the metadata remains unchanged in Solr, unless we run index-discovery. Since we intend to work with a large amount of data, running index-discovery everytime a metadata is edited, would be expensive. Could someone suggest a workaround/solution for this?

You could trigger an item update in the Java class of the REST endpoint.
For example:
In method addItemMetadata of java class org.dspace.rest.ItemsResource which represents the /items REST endpoint you could add the following line after the item metadata has been changed:
itemService.update(context, dspaceItem);
This line of code triggers an index update for that specific item.
This is what the complete addItemMetadata method will look like after the above change:
#POST
#Path("/{item_id}/metadata")
#Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response addItemMetadata(#PathParam("item_id") String itemId, List<org.dspace.rest.common.MetadataEntry> metadata,
#QueryParam("userIP") String user_ip, #QueryParam("userAgent") String user_agent,
#QueryParam("xforwardedfor") String xforwardedfor, #Context HttpHeaders headers, #Context HttpServletRequest request)
throws WebApplicationException
{
log.info("Adding metadata to item(id=" + itemId + ").");
org.dspace.core.Context context = null;
try
{
context = createContext();
org.dspace.content.Item dspaceItem = findItem(context, itemId, org.dspace.core.Constants.WRITE);
writeStats(dspaceItem, UsageEvent.Action.UPDATE, user_ip, user_agent, xforwardedfor, headers, request, context);
for (MetadataEntry entry : metadata)
{
// TODO Test with Java split
String data[] = mySplit(entry.getKey()); // Done by my split, because of java split was not function.
if ((data.length >= 2) && (data.length <= 3))
{
itemService.addMetadata(context, dspaceItem, data[0], data[1], data[2], entry.getLanguage(), entry.getValue());
}
}
itemService.update(context, dspaceItem);
context.complete();
}
catch (SQLException e)
{
processException("Could not write metadata to item(id=" + itemId + "), SQLException. Message: " + e, context);
}
catch (ContextException e)
{
processException("Could not write metadata to item(id=" + itemId + "), ContextException. Message: " + e.getMessage(),
context);
} catch (AuthorizeException e) {
processException("Could not update item(id=" + itemId + "), AuthorizeException. Message: " + e.getMessage(),
context);
} finally
{
processFinally(context);
}
log.info("Metadata to item(id=" + itemId + ") were successfully added.");
return Response.status(Status.OK).build();
}

Related

Salesforce Code Coverage Failure. Your code coverage is 12%. You need at least 75% coverage to complete this deployment

I wanted to deploy my code to production. In this apex code, I am calling a third party api for opportunity on click of button which triggers the doSomething() from VF page. I want to fix this issue and push the below code to my production account.
Here is my apex class code
{
private ApexPages.StandardController standardController;
public DetailButtonController(ApexPages.StandardController standardController)
{
this.standardController = standardController;
}
public PageReference doSomething()
{
// Apex code for handling record from a Detail page goes here
Id recordId = standardController.getId();
Opportunity record = (Opportunity) standardController.getRecord();
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('https://mergeasy.com/merge_file');
req.setMethod('POST');
//function to Convert date to mm/dd/yyy
Date dToday = record.Closing_Date__c;
String clos_date = 'On or before ' + dToday.month() + '/' + dToday.day() + '/' + dToday.year();
Date dAcc = record.Offer_Acceptance_Date__c;
String acc_date = dAcc.month() + '/' + dAcc.day() + '/' + dAcc.year();
String str1 = '' + record.Purchase_Price__c ;
String f_p_price = str1.SubStringBefore('.');
String str2 = '' + record.Escrow_Deposit__c ;
String e_d_price = str2.SubStringBefore('.');
String str3 = '' + record.Balance__c ;
String b_price = str3.SubStringBefore('.');
if(record.Second_Seller_Name_Phone__c==null && record.Second_Seller_Email__c==null && record.Name!=null && record.Company_Profile__c!=null){
req.setBody('seller_name='+record.Name+'&buyer_name='+record.Company_Profile__c+'&county='+record.County_Contract__c+'&street_address='+record.Left_Main__Address_1__c+'&p_price='+f_p_price+'&escrow_deposit='+e_d_price+'&title_agent='+record.Escrow_Agent_Name__c+'&title_address='+record.Escrow_Address__c+'&title_phone='+record.Escrow_Number__c+'&balance='+b_price+'&accept_date='+acc_date+'&closing_date='+clos_date+'&inspection_days='+record.Inspection_Days__c+'&special_clause='+record.Special_Clauses__c+'&doc_id=XXXXXXXXXX&doc_name=Contract.pdf&delivery_method=docusign&sign_order=true&recipient1_email='+record.Email__c+'&recipient1_name='+record.Name+'&recipient2_name='+record.Company_Profile__c+'&recipient2_email=developer.c2c#gmail.com&docusign_doc_name=Contract - Attorney Involved&email_subject=Contract:'+record.Left_Main__Address_1__c+'&email_body=Hi please sign the attached contract');
}
else if(record.Second_Seller_Name_Phone__c!=null && record.Second_Seller_Email__c!=null && record.Name!=null && record.Company_Profile__c!=null){
String name = record.Name + ' and ' + record.Second_Seller_Name_Phone__c ;
req.setBody('seller_name='+name+'&buyer_ame='+record.Company_Profile__c+'&county='+record.County_Contract__c+'&street_address='+record.Left_Main__Address_1__c+'&p_price='+f_p_price+'&escrow_deposit='+e_d_price+'&title_agent='+record.Escrow_Agent_Name__c+'&title_address='+record.Escrow_Address__c+'&title_phone='+record.Escrow_Number__c+'&balance='+b_price+'&accept_date='+acc_date+'&closing_date='+clos_date+'&inspection_days='+record.Inspection_Days__c+'&special_clause='+record.Special_Clauses__c+'&doc_id=XXXXXXXXXX&doc_name=Contract.pdf&delivery_method=docusign&sign_order=true&recipient1_email='+record.Email__c+'&recipient1_name='+record.Name+'&recipient2_name='+record.Second_Seller_Name_Phone__c+'&recipient2_email='+record.Second_Seller_Email__c+'&recipient3_email=developer.c2c#gmail.com&recipient3_name='+record.Company_Profile__c+'&docusign_doc_name=Contract - Normal(1S1B).pdf&email_subject=Contract:'+record.Left_Main__Address_1__c+'&email_body=Hi please sign the attached contract');
}
req.setHeader('Authorization', 'Bearer XXXXXXXXXXXXXX');
try {
res = http.send(req);
} catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
return null;
}
}
Here is the test class, which is showing 90% code coverage.
//testClasst.apxc
#isTest
public class testClassBt {
#isTest
static void testPostCallout() {
System.Test.setMock(HttpCalloutMock.class, new TestClass());
Opportunity opp = new Opportunity();
opp.Name='Rickson Developer';
opp.StageName='Underwrite';
opp.CloseDate= date.newInstance(1991, 2, 21);
opp.Closing_Date__c= date.newInstance(1991, 2, 21);
opp.Offer_Acceptance_Date__c =date.newInstance(1991, 2, 21);
opp.Purchase_Price__c = 1200.00;
opp.Escrow_Deposit__c= 1200.00;
opp.Company_Profile__c='RFTA Properties, LLC';
opp.County_Contract__c='Orange';
opp.Left_Main__Address_1__c='123 Main Street';
opp.Escrow_Agent_Name__c='Test Agent';
opp.Escrow_Address__c='123 Main street';
opp.Escrow_Number__c='9892132382';
opp.Inspection_Days__c=34;
opp.Special_Clauses__c='Test';
insert opp;
ApexPages.StandardController standardController = new ApexPages.StandardController(opp);
DetailButtonController strResp = new DetailButtonController(standardController);
strResp.doSomething();
}
}
//TestClass.apxc
#isTest
global class TestClass implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest request) {
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setBody('{"animal": {"id":1, "name":"Tiger"}}');
response.setStatusCode(200);
return response;
}
}
assuming that during the validation process you run just the test methods of this class, did you try to run your test class in Sandbox first?
Some IDE and the Salesforce Developer Console itself show you the covered lines after the unit test execution.
Just follow the green lines to debug the code and understand where the exception has been thrown.
If you could post the Test class too, we can help you more.

Manage http error codes with Codename One

I wrote:
private RequestBuilder getPostRequest(String api) {
return Rest.post(url + api)
.jsonContent()
.header("wsc-access-key", WowzaAccount.getAccessKey())
.header("wsc-api-key", WowzaAccount.getRestKey());
}
getPostRequest("live_streams").body(json).fetchAsJsonMap(new OnComplete<Response<Map>>() {
#Override
public void completed(Response<Map> v) {
if (v.getResponseCode() == 201) {
// success
Map<String, Object> response = v.getResponseData();
name = (String) response.get("name");
id = (String) response.get("id");
connection_code = (String) response.get("connection_code");
Log.p("WowzaLiveStream -> (Code 201) Successfully created live stream with name " + name, Log.DEBUG);
onComplete.completed(instance);
} else if (v.getResponseCode() == 401) {
Log.p("WowzaLiveStream -> (Code 401) Unauthorized, failed to create live stream with name " + params.name.get(), Log.DEBUG);
onFail.run();
} else if (v.getResponseCode() == 422) {
Log.p("WowzaLiveStream -> (Code 422) Unprocessable Entity, failed to create live stream with name " + params.name.get(), Log.DEBUG);
onFail.run();
} else {
Log.p("WowzaLiveStream -> Unknow response with code " + v.getResponseCode() + ", failed to create live stream with name " + params.name.get(), Log.DEBUG);
onFail.run();
}
}
});
The problem is that when I get a 422 response code my onFail callback is not called. Instead a Dialog appears. I suppose that this dialog is invoked by the default addNetworkErrorListener code in the init(). However... I cannot (and I don't want to) disable the default addNetworkErrorListener code, because I'm writing a new CN1Lib. Instead I need that in this case, and only in this case, the network error listener should not be invoked and instead the failure callback that I wrote should be run.
It's more appropriate, in this case, to call the network error listener only if the Internet connection is lost.
Thank you
You need to explicitly catch the error code callback as the callback might have a different format than the main JSON:
private RequestBuilder getPostRequest(String api) {
return Rest.post(url + api)
.jsonContent()
.header("wsc-access-key", WowzaAccount.getAccessKey())
.header("wsc-api-key", WowzaAccount.getRestKey())
.onErrorCodeJSON(map -> {
// process error response
});
}

Apache Camel - Create a custom Component/Endpoint?

I need to consume messages from a Websocket, but I have to do some logics before consume the data, so I can't use Webscoket Component.
I have a java code that do Authentication in this Websocket and subscribe a "Sensor" to receive data.
Can I create a Camel Component that I use this code in from() and every time I receive new data onNext() the Camel starts the process?
WebSocket webSocket = new WebSocket(uri, apiKey, (api, authenthication) -> {
console.println("Authenticated successfully as " + authenthication.getUserName());
String[] sensors = {sensorId};
api.getMetrics(sensors).subscribe(metrics -> {
Metric[] allMetrics = metrics.get(sensorId);
Arrays.sort(allMetrics, (metric1, metric2) -> metric1.getId().compareTo(metric2.getId()));
Metric firstMetric = allMetrics[0];
console.println("Metric: " + firstMetric.getDisplayName());
String metricId = firstMetric.getId();
String[] metric = {metricId};
api.getUnits(metric).subscribe(units -> {
Unit unit = units.get(metric[0])[0];
console.println("Unit: " + unit.getName());
Instant now = Instant.now();
Instant aMinuteAgo = now.minus(timeInterval, ChronoUnit.SECONDS);
Date start = Date.from(aMinuteAgo);
Date end = Date.from(now);
api.getData(sensorId, metricId, unit.getId(), emptyMap(), start, end).subscribe(new DisposableObserver<Data>() {
#Override
public void onNext(Data data) {
console.println("Data from last " + timeInterval + " seconds: ");
console.println(data.getData());
}
#Override
public void onComplete() {
console.println("Data update:");
Disposable subscription = api.subscribeData(sensors, metricId, unit.getId()).subscribe(updates -> {
console.println(updates.getData());
});
ScheduledExecutorService scheduler = newSingleThreadScheduledExecutor(daemonThreadFactory);
scheduler.schedule(subscription::dispose, cancelDelay, SECONDS);
}
#Override
public void onError(Throwable error) {
error.printStackTrace();
}
});
});
});
});
console.println("Connection was closed by server.");
}

Set field Accessibility to Custom Salesforce Lead field from Java code

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.

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();
}

Resources