Acumatica: How to create new customer in ruby? - ruby

I need to create Customer using SOAP API in ruby (we want to consume Acumatica api from Ruby on Rails project).
Currently my code using Savon gem looks like this:
client = Savon.client(wsdl: 'wsdl.wsdl') # sample wsdl path
response = client.call :login, message: { name: '', password: '' }
auth_cookies = response.http.cookies
class ServiceRequest
def to_s
builder = Builder::XmlMarkup.new
builder.instruct!(:xml, encoding: 'UTF-8')
# ... problem is here, I don't know how XML request should look like
builder
end
end
p client.call :submit, message: ServiceRequest.new, cookies: auth_cookies
Problem is that, I don't know how XML request should look like.
C# requests looks like this (just piece of sample from docs):
PO302000result = context.PO302000Submit(
new Command[]
{ new Value { Value = "PORE000079", LinkedCommand =
PO302000.DocumentSummary.ReceiptNbr},
new Value { Value = "OK", LinkedCommand =
PO302000.AddPurchaseOrderLine.ServiceCommands.DialogAnswer,
PO302000.Actions.AddPOOrderLine, new Key { Value = "='PORG000084'", FieldName = Commit = true },
PO302000.AddPurchaseOrderLine.OrderNbr.FieldName, ObjectName =
PO302000.AddPurchaseOrderLine.OrderNbr.ObjectName },
new Key { Value = "='CPU00004'", FieldName =
PO302000.AddPurchaseOrderLine.InventoryID.FieldName, ObjectName =
PO302000.AddPurchaseOrderLine.InventoryID.ObjectName },
new Value{ Value = "True", LinkedCommand =
PO302000.AddPurchaseOrderLine.Selected, Commit = true },
PO302000.Actions.AddPOOrderLine2
new Key{ Value = "='CPU00004'", FieldName =
PO302000.DocumentDetails_.InventoryID.FieldName, ObjectName =
PO302000.DocumentDetails_.InventoryID.ObjectName},
new Value{ Value = "1.00", LinkedCommand =
PO302000.DocumentDetails_.ReceiptQty, Commit = true},
// the next part of code is needed if you use Serial items
PO302000.BinLotSerialNumbers.ServiceCommands.NewRow,
new Value { Value = "R01", LinkedCommand =
PO302000.BinLotSerialNumbers.Location },
PO302000.Actions.Save
} );
But I don't know what kind of XML this code produce. It looks like we have Commands array with Values and then action name. But what XML does this kind of code renders? Maybe some C# or Java folks can copy me xml requests samples that are rendered by that kind of code?
Thank you a lot.

Basically it's a bad idea to generate XML SOAP package manually, you should have some wrapper on your side, which have to simplify your code.

Anyway, the C# code below + XML SOAP request
Content[] result = context.Submit(
new Command[]
{
new Value { Value = "PORE000079", LinkedCommand = PO302000.DocumentSummary.ReceiptNbr}
,new Value { Value = "OK", LinkedCommand = PO302000.AddPurchaseOrderLine.ServiceCommands.DialogAnswer, Commit = true }
,PO302000.Actions.AddPOOrderLine
,new Key { Value = "='PORG000077'", FieldName = PO302000.AddPurchaseOrderLine.OrderNbr.FieldName, ObjectName = PO302000.AddPurchaseOrderLine.OrderNbr.ObjectName }
,new Key { Value = "='CPU00004'", FieldName = PO302000.AddPurchaseOrderLine.InventoryID.FieldName, ObjectName = PO302000.AddPurchaseOrderLine.InventoryID.ObjectName }
,new Value{ Value = "True", LinkedCommand = PO302000.AddPurchaseOrderLine.Selected, Commit = true }
,PO302000.Actions.AddPOOrderLine2
,new Key{ Value = "='CPU00004'", FieldName = PO302000.DocumentDetails.InventoryID.FieldName, ObjectName = PO302000.DocumentDetails.InventoryID.ObjectName}
,new Value{ Value = "1.00", LinkedCommand = PO302000.DocumentDetails.ReceiptQty, Commit = true}
// the next part of code is needed if you use Serial items
,PO302000.BinLotSerialNumbers.ServiceCommands.NewRow
,new Value { Value = "R01", LinkedCommand = PO302000.BinLotSerialNumbers.Location }
,new Value { Value = "1.00", LinkedCommand = PO302000.BinLotSerialNumbers.Quantity, Commit = true }
,new Value { Value = "25.00", LinkedCommand = PO302000.DocumentDetails.UnitCost, Commit = true }
,new Key { Value = "='CPU00004'", FieldName = PO302000.DocumentDetails.InventoryID.FieldName, ObjectName = PO302000.DocumentDetails.InventoryID.ObjectName }
,new Value { Value = "0.00", LinkedCommand = PO302000.DocumentDetails.ReceiptQty, Commit = true }
,PO302000.Actions.Save
}
);
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><Submit xmlns="http://www.acumatica.com/typed/"><commands><Command xsi:type="Value"><Value>PORE000079</Value><LinkedCommand xsi:type="Field"><FieldName>ReceiptNbr</FieldName><ObjectName>Document</ObjectName><Value>ReceiptNbr</Value><Commit>true</Commit><LinkedCommand xsi:type="Action"><FieldName>cancel</FieldName><ObjectName>Document</ObjectName><LinkedCommand xsi:type="Key"><FieldName>ReceiptNbr</FieldName><ObjectName>Document</ObjectName><Value>=[Document.ReceiptNbr]</Value><LinkedCommand xsi:type="Key"><FieldName>ReceiptType</FieldName><ObjectName>Document</ObjectName><Value>=[Document.ReceiptType]</Value></LinkedCommand></LinkedCommand></LinkedCommand></LinkedCommand></Command><Command xsi:type="Value"><Value>OK</Value><Commit>true</Commit><LinkedCommand xsi:type="Answer"><ObjectName>poLinesSelection</ObjectName><Value>='Yes'</Value></LinkedCommand></Command><Command xsi:type="Action"><FieldName>AddPOOrderLine</FieldName><ObjectName>Document</ObjectName><Commit>true</Commit></Command><Command xsi:type="Key"><FieldName>OrderNbr</FieldName><ObjectName>poLinesSelection</ObjectName><Value>='PORG000077'</Value></Command><Command xsi:type="Key"><FieldName>InventoryID</FieldName><ObjectName>poLinesSelection</ObjectName><Value>='CPU00004'</Value></Command><Command xsi:type="Value"><Value>True</Value><Commit>true</Commit><LinkedCommand xsi:type="Field"><FieldName>Selected</FieldName><ObjectName>poLinesSelection</ObjectName><Value>Selected</Value><Commit>true</Commit></LinkedCommand></Command><Command xsi:type="Action"><FieldName>AddPOOrderLine2</FieldName><ObjectName>Document</ObjectName><Commit>true</Commit></Command><Command xsi:type="Key"><FieldName>InventoryID</FieldName><ObjectName>transactions</ObjectName><Value>='CPU00004'</Value></Command><Command xsi:type="Value"><Value>1.00</Value><Commit>true</Commit><LinkedCommand xsi:type="Field"><FieldName>ReceiptQty</FieldName><ObjectName>transactions</ObjectName><Value>ReceiptQty</Value><Commit>true</Commit></LinkedCommand></Command><Command xsi:type="NewRow"><ObjectName>splits</ObjectName></Command><Command xsi:type="Value"><Value>R01</Value><LinkedCommand xsi:type="Field"><FieldName>LocationID</FieldName><ObjectName>splits</ObjectName><Value>Location</Value></LinkedCommand></Command><Command xsi:type="Value"><Value>1.00</Value><Commit>true</Commit><LinkedCommand xsi:type="Field"><FieldName>Qty</FieldName><ObjectName>splits</ObjectName><Value>Quantity</Value></LinkedCommand></Command><Command xsi:type="Value"><Value>25.00</Value><Commit>true</Commit><LinkedCommand xsi:type="Field"><FieldName>CuryUnitCost</FieldName><ObjectName>transactions</ObjectName><Value>UnitCost</Value></LinkedCommand></Command><Command xsi:type="Key"><FieldName>InventoryID</FieldName><ObjectName>transactions</ObjectName><Value>='CPU00004'</Value></Command><Command xsi:type="Value"><Value>0.00</Value><Commit>true</Commit><LinkedCommand xsi:type="Field"><FieldName>ReceiptQty</FieldName><ObjectName>transactions</ObjectName><Value>ReceiptQty</Value><Commit>true</Commit></LinkedCommand></Command><Command xsi:type="Action"><FieldName>Save</FieldName><ObjectName>Document</ObjectName></Command></commands></Submit></soap:Body></soap:Envelope>

So in the end what I did:
gem install 'mumboe-soap4r' # not soap4r,
# because soap4r is old and bugged with newer rubies
Then I ran
wsdl2ruby.rb --wsdl customer.wsdl --type client
Where wsdl2ruby.rb is installed together with mumboe-soap4r gem. Replace customer.wsdl with path to your wsdl, could be URL or file system path.
After running this command next files were created:
default.rb
defaultMappingRegistry.rb
defaultDriver.rb
ScreenClient.rb
Using those files you can write code similar to C# code or php code to interact with acumatica API:
require_relative 'defaultDriver'
require 'soap/wsdlDriver'
# this is my helper method to make life easier
def prepare_value(value, command, need_commit = false, ignore = false)
value_command = Value.new
value_command.value = value
value_command.linkedCommand = command
value_command.ignoreError = ignore unless ignore.nil?
value_command.commit = need_commit unless need_commit.nil?
value_command
end
soap_client = SOAP::WSDLDriverFactory.new('customer.wsdl').create_rpc_driver
soap_client.login(name: '', password: '').loginResult
screen = soap_client.getSchema(nil)
soap_client.clear(nil)
content = screen.getSchemaResult
# p schema
p customer = content.customerSummary.customerID
p customer_name = content.customerSummary.customerName
country = content.generalInfoMainAddress.country
customer_class = content.generalInfoFinancialSettings.customerClass
commands = ArrayOfCommand.new
commands << prepare_value('ABBA', customer_name)
commands << prepare_value('US', country)
commands << prepare_value('MERCHANT', customer_class)
commands << content.actions.insert
commands << customer.clone # to return
p commands
p soap_client.submit(commands)
Hope it will help someone.

Actually 'mumboe-soap4r' or 'soap2r' or 'soap4r' doesn't work with Acumatica soap API. They are too old and buggy.
In the end I am using Savon gem (version 2). I am creating message using XmlMarkup class. But how I know what XML should I create? In order to know this I am creating soap request in .net , then I see what right XML request looks like and only then I am creating soap request using Savon gem. Too much work, but I don't know better way for now. It works.
In order for Savon to work with Acumatica API I set next options:
client = Savon.client do
wsdl 'http://path/Soap/AR303000.asmx?wsdl'
log true
namespaces 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/'
env_namespace 'soap'
namespace_identifier nil
element_form_default ''
end
Don't forget to pass auth cookies
response = client.call(:login, message: { name: '', password: '' })
auth_cookies = response.http.cookies
Build your customer creating xml, then do submit
m = build_create_submit(customer_name)
response = client.call(:submit, message: m, cookies: auth_cookies)
# get customer id of newly created customer
customer_id = response.try(:hash).try(:[], :envelope).
try(:[], :body).
try(:[], :submit_response).
try(:[], :submit_result).
try(:[], :content).
try(:[], :customer_summary).
try(:[], :customer_id).
try(:[], :value)

Related

Getting terraform error: Error: "expected create_option to be one of [Attach Empty], got FromImage"

I am trying to create azure cyclecloud server using terraform , my code is below
note: remove the actual disk id ,vm id, public key etc
resource "azurerm_virtual_machine_data_disk_attachment" "res-0" {
caching = "None"
create_option = "FromImage"
lun = 0
managed_disk_id = "Disk_id"
virtual_machine_id = "vm_id"
depends_on = [
azurerm_linux_virtual_machine.res-0,
]
}
resource "azurerm_linux_virtual_machine" "res-0" {
admin_username = "cyclecloud"
location = "westus2"
name = "cc3"
network_interface_ids = network_interfaces_id"
resource_group_name = "myrg"
size = "Standard_DS1_v2"
admin_ssh_key {
public_key = "my_public_key"
username = "cyclecloud"
}
boot_diagnostics {
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
plan {
name = "cyclecloud-81"
product = "azure-cyclecloud"
publisher = "azurecyclecloud"
}
source_image_reference {
offer = "azure-cyclecloud"
publisher = "azurecyclecloud"
sku = "cyclecloud-81"
version = "latest"
}
}
while running :
terraform apply ,getting the below error:
Error: expected create_option to be one of [Attach Empty], got FromImage
with azurerm_virtual_machine_data_disk_attachment.res-0,
on main.tf line 3, in resource "azurerm_virtual_machine_data_disk_attachment" "res-0":
3: create_option = "FromImage"
Please assist
note i am using the below provider:
terraform {
backend "local" {}
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "3.31.0"
}
}
}
provider "azurerm" {
features {}
}
The error clearly says that you are passing an unsupported value to the create_option attribute of resource azurerm_virtual_machine_data_disk_attachment. The possible values are Empty or Attach.
resource "azurerm_virtual_machine_data_disk_attachment" "res-0" {
caching = "None"
create_option = "Empty" ## or ## "Attach" # <- Choose any of these values.
lun = 0
managed_disk_id = "Disk_id"
virtual_machine_id = "vm_id"
depends_on = [
azurerm_linux_virtual_machine.res-0,
]
}
Refer to attribute description:
create_option - (Optional) The Create Option of the Data Disk, such as Empty or Attach. Defaults to Attach. Changing this forces a new resource to be created.
Official Documentation: https://registry.terraform.io/providers/hashicorp/azurerm/3.31.0/docs/resources/virtual_machine_data_disk_attachment#create_option
After this may be network_interface_ids = network_interfaces_id" this might result in an error if no real network_interface_ids are provided as the attribute values.
Refer to official hashicorp example : https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_virtual_machine
Not Sure but if the goal is to attach an additional data disk to the machine then azurerm_managed_disk
resource is also required not only just attachment and there you can use create_option = FromImage attribute value.
create_option for the azurerm_managed_disk : https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/managed_disk#create_option

Jenkins read json file with multiple list value jsonsurper or readjson

I want to be able ro read json format based on parameter value selected in choice. e.g If dev is selected, it should select (dev1,dev2,dev3) and loop through each selected in json through the node. what is important now is to get the value in json to a file and then I call call it from file into the node
error:
groovy.lang.MissingMethodException: No signature of method: net.sf.json.JSONObject.$() is applicable for argument types: (org.jenkinsci.plugins.workflow.cps.CpsClosure2) values: [org.jenkinsci.plugins.workflow.cps.CpsClosure2#7e1eb88f]
Possible solutions: is(java.lang.Object), any(), get(java.lang.Object), get(java.lang.String), has(java.lang.String), opt(java.lang.String)
script in pipeline
#!/usr/bin/env groovy
node{
properties([
parameters([
choice(
name: 'environment',
choices: ['','Dev', 'Stage', 'devdb','PreProd','Prod' ],
description: 'environment to choose'
),
])
])
node () {
def myJson = '''{
"Dev": [
"Dev1",
"Dev2",
"Dev3"
],
"Stage": [
"Stage1",
"Stage2"
],
"PreProd": [
"Preprod1"
],
"Prod": [
"Prod1",
"Prod2"
]
}''';
def myObject = readJSON text: myJson;
echo myObject.${params.environment};
// put the list of the node in a file or in a list to loop
}
Using Pipeline Utility Steps, it can be easier:
Reading from string:
def obj = readJSON text: myjson
Or reading from file:
def obj = readJSON file: 'myjsonfile.json'
Now you can get the element and iterate the list:
def list = obj[params.environment]
list.each { elem ->
echo "Item: ${elem}"
}
Reference: https://www.jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#readjson-read-json-from-files-in-the-workspace
Let me make it simple.
To read the json file you need to download it from git or wherever you stored it. Let's assume git in this case. Once the json file is downloaded then you want to access the content of json file in your code. Which can be done by this code.
import groovy.json.JsonSlurperClassic
def downloadConfigFile(gitProjectURL, jsonnFileBranch) {
// Variables
def defaultBranch = jsonnFileBranch
def gitlabAdminCredentials = 'admin'
def poll = false
def jenkinsFilePath = 'jenkins.json'
// Git checkout
git branch: defaultBranch, credentialsId: gitlabAdminCredentials, poll: poll, url: gitProjectURL
// Check if file existed or not
def jenkinsFile = fileExists(jenkinsFilePath)
if (jenkinsFile) {
def jsonStream = readFile(jenkinsFilePath)
JsonSlurperClassic slurper = new JsonSlurperClassic()
def parsedJson = slurper.parseText(jsonStream)
return parsedJson
} else {
return [:]
}
}
Now we have the entire json file parsed using above function.
Now you can call the function and read the value in a global variable.
stage('Download Config') {
jsonConfigData = downloadConfigFile(gitProjectURL, jsonnFileBranch)
if (jsonConfigData.isEmpty()) {
error err_jenkins_file_does_not_exists
} else {
println("jsonConfigData : ${jsonConfigData}")
}
Now you can access the value of json file or say variable like this.
def projectName = jsonConfigData.containsKey('project_name') == true ? jsonConfigData['project_name'] : ''
You can access any thing if its child node in similar way. I hope it helps you.

How to get image classification prediction from GCP AIPlatform in ruby?

I'm new with ruby and I want to use GCP AIPlatform but I'm struggeling with the payload.
So far, I have :
client = ::Google::Cloud::AIPlatform::V1::PredictionService::Client.new do |config|
config.endpoint = "#{location}-aiplatform.googleapis.com"
end
img = File.open(imgPath, 'rb') do |img|
'data:image/png;base64,' + Base64.strict_encode64(img.read)
end
instance = Instance.new(:content => img)
request = Google::Cloud::AIPlatform::V1::PredictRequest.new(
endpoint: "projects/#{project}/locations/#{location}/endpoints/#{endpoint}",
instances: [instance]
)
result = client.predict request
p result
Here is my proto
message Instance {
required bytes content = 1;
};
But I have the following error : Invalid type Instance to assign to submessage field 'instances'
I read the documentation but for ruby SDK it's a bit light.
The parameters are OK, the JS example here : https://github.com/googleapis/nodejs-ai-platform/blob/main/samples/predict-image-object-detection.js is working with those parameters
What am I doing wrong ?
I managed it
client = Google::Cloud::AIPlatform::V1::PredictionService::Client.new do |config|
config.endpoint = "#{location}-aiplatform.googleapis.com"
end
img = File.open(imgPath, 'rb') do |img|
Base64.strict_encode64(img.read)
end
instance = Google::Protobuf::Value.new(:struct_value => {:fields => {
:content => {:string_value => img}
}})
endpoint = "projects/#{project}/locations/#{location}/endpoints/#{endpoint}"
request = Google::Cloud::AIPlatform::V1::PredictRequest.new(
endpoint: endpoint,
instances: [instance]
)
result = client.predict request
p result
The use of the Google::Protobuf::Value looks ugly to me but it works

Error in using Solr to add data - Solr HTTP error: OK (409)(HttpException )

I am trying this out for quite a time now, I have even googled a lot.
I am getting this error while trying to add data into Solr using Solarium in Laravel,
(1/1) HttpException
Solr HTTP error: OK (409)
{
"responseHeader":{
"status":409,
"QTime":3},
"error":{
"metadata":[
"error-class","org.apache.solr.common.SolrException",
"root-error-class","org.apache.solr.common.SolrException"],
"msg":"version conflict for 12 expected=12435421423451 actual=-1",
"code":409}}
in Result.php line 106
at Result->__construct(object(Client), object(Query), object(Response))in Client.php line 753
This is my function in EmployeeController.php
public function enterDataSolr()
{
$update = $this->client->createUpdate();
$doc1 = $update->createDocument();
$doc1->Gender = "M";
$doc1->Salary = 199999;
$doc1->SSN = "0050-03-10T21:00:00Z";
$doc1->City = "Mumbai";
$doc1->State = "Maharastra";
$doc1->Zip = 119973;
$doc1->Region = "Navi Mumbai";
$doc1->Password = "21435t34tgsd";
$doc1->id = 12;
$doc1->_Emp_ID = 1234546;
$doc1->Name_Prefix = "Mr.";
$doc1->First_Name = "Kant";
$doc1->Middle_Initial = "S";
$doc1->Last_Name = "Bhat";
$doc1->E_Mail = "nav#gmail.com";
$doc1->Father_s_Name = "Mant";
$doc1->Mother_s_Name = "Vandana";
$doc1->Mother_s_Maiden_Name = "vandana";
$doc1->Date_of_Birth = 12/2/1998;
$doc1->Time_of_Birth = "12:24";
$doc1->Age_in_Yrs = 21;
$doc1->Weight_in_Kgs = 56;
$doc1->Date_of_Joining = "2/2/2020";
$doc1->Quarter_of_Joining = "Q1";
$doc1->Half_of_Joining = "1st";
$doc1->Year_of_Joining = 2020;
$doc1->Month_of_Joining = 2;
$doc1->Month_Name_of_Joining = "February";
$doc1->Short_Month = "Feb";
$doc1->Day_of_Joining = 2;
$doc1->DOW_of_Joining = "Tuesday";
$doc1->Short_DOW = "Tues";
$doc1->Age_in_Company__Years_ = 2.4;
$doc1->Last___Hike = 2;
$doc1->Phone_No = 8906986022;
$doc1->Place_Name = "Delhi";
$doc1->User_Name = "kant";
$doc1->_version_ = 12435421423451;
$doc1->score = 1;
$doc2 = $update->createDocument();
$doc2->Gender = "F";
$doc2->Salary = '200000';
$doc2->SSN = "0050-03-10T00:00:00Z";
$doc2->City = "Purcellville";
$doc2->State = "VA";
$doc2->Zip = 20134;
$doc2->Region = "South";
$doc2->Password = "1";
$doc2->id = "2a69b460-2299-46a6-84b6-cf16938a1997";
$doc2->_Emp_ID = 520092;
$doc2->Name_Prefix = "Mrs.";
$doc2->First_Name = "Mary";
$doc2->Middle_Initial = "Watson";
$doc2->Last_Name = "Jane";
$doc2->E_Mail = "janemarie#hotmail.com";
$doc2->Father_s_Name = "Spder";
$doc2->Mother_s_Name = "May";
$doc2->Mother_s_Maiden_Name = "may";
$doc2->Date_of_Birth = "10/1/1921";
$doc2->Time_of_Birth = "12:02";
$doc2->Age_in_Yrs = 99;
$doc2->Weight_in_Kgs = 61;
$doc2->Date_of_Joining = "2/27/2020";
$doc2->Quarter_of_Joining = "Q2";
$doc2->Half_of_Joining = "Q1";
$doc2->Year_of_Joining = "Q4";
$doc2->Month_of_Joining = "2";
$doc2->Month_Name_of_Joining = "February";
$doc2->Short_Month = "Feb";
$doc2->Day_of_Joining = 27;
$doc2->DOW_of_Joining = "Tuesday";
$doc2->Short_DOW = "Tues";
$doc2->Age_in_Company__Years_ = 1.7;
$doc2->Last___Hike = "11%";
$doc2->Phone_No = 852489628962;
$doc2->Place_Name = "Purcellville";
$doc2->User_Name = "llwoods";
$doc2->_version_ = 1658322049611851997;
$doc2->score = 1;
$update->addDocuments(array($doc1, $doc2));
$update->addCommit();
$result = $this->client->update($update);
echo '<b>Update query executed</b><br/>';
echo 'Query status: ' . $result->getStatus(). '<br/>';
echo 'Query time: ' . $result->getQueryTime();
}
The connection is made properly as as ping() function is returning status OK.
The search function is working properly as well.
This is the constructor
public function __construct(EmployeeRepository $emp_repository, Client $client)
{
$this->emp_repository = $emp_repository;
$this->client = $client;
//dd('Solarium library version: ' . Client::VERSION . ' - ');
}
and I have used class as well
use Solarium\Client;
Optimistic Concurrency is a feature of Solr that can be used by client applications which update/replace documents to ensure that the document
they are replacing/updating has not been concurrently modified by another client application.
If there is a version conflict (HTTP error code 409), the client starts the process over.
This feature works by requiring a _version_ field on all documents in the index, and comparing that to a version specified as part of the update command.
By default, Solr’s Schema includes a _version_ field, and this field is automatically added to each new document.
$ curl -X POST -H 'Content-Type: application/json' 'http://localhost:8983/solr/techproducts/update?_version_=1632740120218042368&versions=true&commit=true&omitHeader=true' --data-binary '
[{ "id" : "aaa",
"foo_s" : "update attempt with correct existing version" }]'
an update with a value for _version_ that matches the value in the index, and it succeeds. Because we included versions=true to the update request,
the response includes a different value for the _version_ field.
If an update with a value for _version_ embedded in the document itself. The request fails because you have specified the wrong version.
Below would be the error for it.
{
"error":{
"metadata":[
"error-class","org.apache.solr.common.SolrException",
"root-error-class","org.apache.solr.common.SolrException"],
"msg":"version conflict for aaa expected=100 actual=1632740462042284032",
"code":409
}
}
Please refer the solr documentation for more details.
The -1 here is meant that Solr is not able to find a document with that version.
I would suggest you to try sending one of the document to solr yourself by hand on the Solr admin UI.
Select your core/collection name, then click the Documents link(on the solr admin page) and you'll be at the page where you could send the document for update to solr.
Solr Document Update

Invalid key error when updating a value of custom field with Ruby rally_api

When I try to update a custom field like this:
#rally.update('defect', 1234567890, {'c_CustomField'=>newValue})
I get :
Error on request - https://rally1.rallydev.com/slm/webservice/v2.0/defect/1234567890.js - {:errors=>["Not authorized to perform action: Invalid key"]}
The error you receive is specific to v2.0 of WS API . See Authorization section in WS API documentation. In v2.0 a security token is required to authorize Create and Update requests.
For example, if you update or create an artifact using an update or create URL, a security token must be obtained first:
GET a security token using this endpoint:
`https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize`
response:
{"OperationResult": {"_rallyAPIMajor": "2", "_rallyAPIMinor": "0", "Errors": [], "Warnings": [], "SecurityToken": "6a4b8....."}}
POST
`https://rally1.rallydev.com/slm/webservice/v2.0/HierarchicalRequirement/create?key=6a4b8...`.
However, rally_api gem 0.9.20 makes it transparent, and there is no need to request a token explicitly.
Here is an example where a custom field is updated:
require 'rally_api'
#Setup custom app information
headers = RallyAPI::CustomHttpHeader.new()
headers.name = "edit custom field"
headers.vendor = "Nick M RallyLab"
headers.version = "1.0"
# Connection to Rally
config = {:base_url => "https://rally1.rallydev.com/slm"}
config[:username] = "user"
config[:password] = "secret"
config[:workspace] = "W"
config[:project] = "P"
config[:version] = "v2.0"
config[:headers] = headers #from RallyAPI::CustomHttpHeader.new()
#rally = RallyAPI::RallyRestJson.new(config)
query = RallyAPI::RallyQuery.new()
query.type = :defect
query.fetch = "Name,FormattedID,CreationDate,Owner,UserName,c_MyCustomField"
query.workspace = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/workspace/1111.js" }
query.project = {"_ref" => "https://rally1.rallydev.com/slm/webservice/v2.0/project/2222.js" }
query.page_size = 200 #optional - default is 200
query.limit = 1000 #optional - default is 99999
query.project_scope_up = false
query.project_scope_down = true
query.order = "Name Asc"
query.query_string = "(FormattedID = DE13)"
results = #rally.find(query)
results.each do |d|
puts "MyCustomField: #{d["c_MyCustomField"]}"
d.read
field_updates = {"c_MyCustomField" => "new text goes here"}
d.update(field_updates)
puts "MyCustomField: #{d["c_MyCustomField"]}"
end

Resources