How to convert a json response to match schema (not manually)? - karate

I'm trying to validate a schema for complex JSON. We can easily compare a schema with API response by below command
And match response == response_SCHEMA
(where "response_SCHEMA" is json schema)
For small json we can manually create:
Actual API response:
{ "id": "123", "name": "abc", "type": "Mumbai", "owner": { "name": "Mr Singh", "type": "Business", "licenseNo": "ASL8989" }
Converted the response to below - manually
{ "id": "#number", "name": "#string", "type": "#string", "owner": { "name": "#string", "type": "#string", "licenseNo": "#string" }
How to create this kind of schema automatically for a complex big json having 300-400 lines? So, we can compare it with API response with Karate.

The point of the schema design is that you can easily cut and paste an actual JSON and either use it as it is (data match, recommended) or edit it to use #string etc (schema match).
When you say 300-400 lines, most likely you mean an array of JSON. All you need to do is specify the schema of the "repeating part" and then use match each: https://github.com/intuit/karate#match-each
* def actual = [{ a: 1, b: 'x' }, { a: 2, b: 'y' }]
* def schema = { a: '#number', b: '#string' }
* match each actual == schema
The short answer is there is no automatic way to do it. Typically you never need to do more than a few lines. Maybe you can write your own custom utility.

#Mayank I also faced this issue, Unfortunately I didn't find any options.
So I created my own small JS to convert the actual JSON into JSON Schema which is compatible with Karate Fuzzy Match.
The output may look like this
enter image description here
Hope This Helps!...
function main() {
var json;
if (document.getElementById('code').value) {
try {
json = JSON.parse(document.getElementById('code').value);
document.getElementById('code').value = JSON.stringify(inputTxt, null, 2);
document.getElementById('output').value = '';
} catch (e) {
document.getElementById('output').value = e;
}
}
let outArr = {};
let output = convertJson(json, 'response', true);
ouput = Object.assign(output, outArr);
Object.keys(ouput).forEach(key => ouput[key] === "#undefined" && delete ouput[key]);
document.getElementById('output').value = JSON.stringify(output, null, 2);
function convertJson(json, keyName, isParent) {
let outA = {};
let x = {};
let y = {};
Object.keys(json).forEach(function(key) {
if (!(getJSType(json[key]) === "object") && !(getJSType(json[key]) === "array")) {
x[key] = '#' + getJSType(json[key]);
}
if (getJSType(json[key]) === "object") {
x[key] = convertJson(json[key], key, false);
}
if (getJSType(json[key]) === "array") {
x[key] = '#' + getJSType(json[key]);
// y[key + 'arr'] = getArray(json[key][0], key, false);
getArray(json[key][0], key, false);
}
})
if (isParent) {
if (Object.keys(x).length > 0) {
outA[keyName] = x;
}
if (Object.keys(y).length > 0) {
outA[keyName + 'Arr'] = y;
}
return outA;
} else {
if (Object.keys(y).length > 0 && isParent) {
return y;
}
if (Object.keys(x).length > 0) {
return x;
}
}
}
function getArray(json, keyName, isParent) {
let z = {};
if (!(getJSType(json) === "object") && !(getJSType(json) === "array")) {
z[keyName + 'Arr'] = '#' + getJSType(json);
} else {
z[keyName + 'Arr'] = convertJson(json, keyName, false);
}
outArr = Object.assign(outArr, z);
}
function getJSType(valToChk) {
function isUndefined(valToChk) {
return valToChk === undefined;
}
function isNull(valToChk) {
return valToChk === null;
}
function isArray(valToChk) {
return valToChk.constructor == Array;
}
function isBoolean(valToChk) {
return valToChk.constructor == Boolean;
}
function isFunction(valToChk) {
return valToChk.constructor == Function;
}
function isNumber(valToChk) {
return valToChk.constructor == Number;
}
function isString(valToChk) {
return valToChk.constructor == String;
}
function isObject(valToChk) {
return valToChk.constructor == Object;
}
if (isUndefined(valToChk)) {
return "undefined";
}
if (isNull(valToChk)) {
return "null";
}
if (isArray(valToChk)) {
return "array";
}
if (isBoolean(valToChk)) {
return "boolean";
}
if (isFunction(valToChk)) {
return "function";
}
if (isNumber(valToChk)) {
return "number";
}
if (isString(valToChk)) {
return "string";
}
if (isObject(valToChk)) {
return "object";
}
}
}
function formatJson() {
if (document.getElementById('code').value) {
try {
var inputTxt = JSON.parse(document.getElementById('code').value);
document.getElementById('code').value = JSON.stringify(inputTxt, null, 2);
document.getElementById('output').value = '';
} catch (e) {
document.getElementById('output').value = e;
}
}
}
function minifyJson() {
if (document.getElementById('code').value) {
try {
var inputTxt = JSON.parse(document.getElementById('code').value);
document.getElementById('code').value = JSON.stringify(inputTxt, null, null);
document.getElementById('output').value = '';
} catch (e) {
document.getElementById('output').value = e;
}
}
}
<html>
<head>
<title>JavaScript Code Runner</title>
</head>
<body>
<h3>Generate the Json Schema for Response</h3>
<div style="display: flex;">
<textarea id="code" style="flex: 1; height: 80vh;" spellcheck="false"></textarea>
<textarea id="output" style="flex: 1; height: 80vh; overflow: auto;" spellcheck="false"></textarea>
</div>
<div style="display: flex;height: 12;"></div>
<button onclick="runCode()">Generate Schema</button>
<button onclick="formatJson()">Format JSON</button>
<button onclick="minifyJson()">Minify JSON</button>
<script src="src/jsonconverter.js"></script>
<script>
function runCode() {
main();
}
</script>
</body>
</html>

Related

I am writing a client side code to retreive a record but facing the below issue

I am writing this code using Xrm.Webapi.RetreiveRecord as below but I am getting the below error when debugging.
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
if (typeof (ContosoPermit) == "undefined") { var ContosoPermit = { __namespace: true }; }
if (typeof (ContosoPermit.Scripts) == "undefined") { ContosoPermit.Scripts = { __namespace: true }; }
ContosoPermit.Scripts.PermitForm = {
handleOnLoad: function (executionContext) {
console.log('on load - permit form');
},
handleOnChangePermitType: function (executionContext)
{
console.log('on change - permit type');
},
_handlePermitTypeSettings: function (executionContext) {
var formContext = executionContext.getFormContext();
var permitType = formContext.getAttribute("contoso_permittype").getValue();
if (permitType == null) {
formContext.ui.tabs.get("inspectionsTab").setVisible(false);
return;
} else {
var permitTypeID = permitType[0].id;
debugger;
Xrm.WebApi.retrieveRecord("contoso_permittype", permitTypeID).then(
function success(result) {
if (result.contoso_requireinspections) {
formContext.ui.tabs.get("inspectionstab").setVisible(true);
}
else {
formContext.ui.tabs.get("inspectionstab").setVisible(false);
}
},
function (error) { alert('Error' + error.message) });
}
},
__namespace: true
}

Validator not validating the request in Laravel 8

I am inserting the data. The data is being entering quite fine but whenever I enter a letter the entry is done but that entry is converted to '0'.
This is my controller store function:
public function store(GuidanceReportRequest $request)
{
$stats = GuidanceReport::where('user_id', $request->user_id)->whereDate('created_at', now())->count();
if ($stats > 0) {
Session::flash('warning', 'Record already exists for current date');
return redirect()->route('reports.index');
}
if ((!empty($request->call_per_day[0]) && !empty($request->transfer_per_day[0])) ||
(!empty($request->call_per_day[1]) && !empty($request->transfer_per_day[1])) || (!empty($request->call_per_day[2])
&& !empty($request->transfer_per_day[2]))
) {
foreach ($request->category as $key => $value) {
$catgeory_id = $request->category[$key];
$call_per_day = $request->call_per_day[$key];
$transfer_per_day = $request->transfer_per_day[$key];
if (!empty($catgeory_id) && !empty($call_per_day) && !empty($transfer_per_day)) {
GuidanceReport::create([
"user_id" => $request->user_id,
"categories_id" => $catgeory_id,
"call_per_day" => $call_per_day,
"transfer_per_day" => $transfer_per_day,
]);
}
}
} else {
GuidanceReport::create($request->except('category', 'call_per_day', 'transfer_per_day'));
}
Session::flash('success', 'Data Added successfully!');
return redirect()->route('reports.index');
}
This is my Validation Request code
public function rules()
{
$rules = [];
$request = $this->request;
if ($request->has('transfer_per_day')) {
if (!empty($request->transfer_per_day)) {
$rules['transfer_per_day'] = "numeric";
}
}
if ($request->has('call_per_day')) {
if (!empty($request->call_per_day)) {
$rules['call_per_day'] = "numeric";
}
}
if ($request->has('rea_sign_up')) {
if (!empty($request->rea_sign_up)) {
$rules['rea_sign_up'] = "numeric";
}
}
if ($request->has('tbd_assigned')) {
if (!empty($request->tbd_assigned)) {
$rules['tbd_assigned'] = "numeric";
}
}
if ($request->has('no_of_matches')) {
if (!empty($request->no_of_matches)) {
$rules['no_of_matches'] = "numeric";
}
}
if ($request->has('leads')) {
if (!empty($request->leads)) {
$rules['leads'] = "numeric";
}
}
if ($request->has('conversations')) {
if (!empty($request->conversations)) {
$rules['conversations'] = "numeric";
}
}
return $rules;
}
Although I check the type in which request is being sent from controller and recieved from the request validation and it is Object. So how can I solve the issue.

How to get data using Add-in program from Office document without metadata(i.e. creation time)?

There's a function which retrieves document of PDF format byte by byte:
Office.initialize = function (reason) {
$(document).ready(function () {
// If not using Word 2016
if (!Office.context.requirements.isSetSupported('WordApi', '1.1')) {
$('#hash-button-text').text("Not supported!");
return;
}
//$('#hash-button').click(calculate_hash);
$('#btn').click(getFile);
});
};
function getFile() {
Office.context.document.getFileAsync(Office.FileType.Pdf, { sliceSize: 99 },
function (result) {
if (result.status == "succeeded") {
var file = result.value;
var sliceCount = file.sliceCount;
var slicesReceived = 0, gotAllSlices = true, docdataSlices = [];
getSliceAsync(file, 0, sliceCount, gotAllSlices, docdataSlices, slicesReceived);
}
else {
console.log("Error");
}
}
);
}
function getSliceAsync(file, nextSlice, sliceCount, gotAllSlices, docdataSlices, slicesReceived) {
file.getSliceAsync(nextSlice, function (sliceResult) {
if (sliceResult.status == "succeeded") {
if (!gotAllSlices) { // Failed to get all slices, no need to continue.
return;
}
docdataSlices[sliceResult.value.index] = sliceResult.value.data;
if (++slicesReceived == sliceCount) {
file.closeAsync();
console.log("Done: ", docdataSlices);
}
else {
getSliceAsync(file, ++nextSlice, sliceCount, gotAllSlices, docdataSlices, slicesReceived);
}
}
else {
gotAllSlices = false;
file.closeAsync();
console.log("getSliceAsync Error:", sliceResult.error.message);
}
});
}
So, close to ~1800 byte there are bytes like "CreationDate(D:20190218150353+02'00..." which are unnecessary in our case.
It retrieves the whole PDF file which consists meta, but is it possible to get it without it?
Best regards
The document.getFileAsync method will always return the entire document (including metadata); it's not possible to make it return anything less than the entire document.

ctools table component conditional formatting

I have no idea if this is the correct forum to ask this question, but I figured I would give it a go - does anyone use Pentaho Ctools? I am trying to apply conditional formatting to column 8 of my table component, but so far no available. Any thoughts would be greatly appreciated!
function f(){
this.setAddInOptions("numeric","formattedText",function(statusReport){
var days = statusReport.value;
if(statusREport.colIndex == 8)
if(days <=30){
return { textFormat: function(v, st) { return "<span style='color:green'>"+v+"</span>"; } };
}
else {
return { textFormat: function(v, st) { return "<span style='color:red'>"+v+"</span>"; } };
}
});
}
Pre Execution Function:
function f(){
//conditional coloring of cells
this.setAddInOptions("colType","formattedText",function(cell_data){
var days = cell_data.value;
if(cell_data.colIdx == 7)
{
if(!cell_data.value) //checking the null possibility
{
this.value = '00000';
}
}
if(days > 30){
return { textFormat: function(v, st) { return "<span style='color:#FF0000'>"+v+"</span>"; } };
}
else if(days <= 30) {
return { textFormat: function(v, st) { return "<span style='color:#000000'>"+v+"</span>"; } };
}
}) ;
}
You also have to update the Column Types in Advanced Properties - make the regular column types "string" or whatever they are and change the formatted column to "formattedText".

Codeception: acceptance test with selenium fails on "textarea"

I've got a function:
public function waitAndFill($element, $value, $timeOut = null)
{
$I = $this;
$I->_waitFor($element, $timeOut);
$I->fillField($element, $value);
$I->seeInField($element, $value);
}
And I use it like this:
$I->waitAndFill('#inputInfo', 'This is test info');
The textarea looks as follows
<textarea id="inputInfo"
name="company_description"
ng-model="company.company_description"
class="form-control"></textarea>
So, my test fails with this:
Step I see in field "#inputInfo","This is test info"
Fail Failed testing for 'This is test info' in company_description's value:
Failed asserting that an array contains 'This is test info'.
It works fine on <input> fields, but fails on <textarea>. Looks like it doesn't see any text at all.
This text is present on the screen shot made by the test.
What am I doing wrong?
It's a bug. The problem is this: $I->seeInField($element, $value); invokes $this->proceedSeeInField(array $elements, $value); which invokes a method getText() on the Facebook\WebDriver\Remote\RemoteWebElement object that it finds. If you read the documentation for Facebook\WebDriver\Remote\RemoteWebElement#getText() method it says that it returns the innerText for that element. So that's no bueno. To workaround this I removed the conditional for the textarea in this function.
protected function proceedSeeInField(array $elements, $value)
{
$strField = reset($elements)->getAttribute('name');
if (reset($elements)->getTagName() === 'select') {
$el = reset($elements);
$elements = $el->findElements(WebDriverBy::xpath('.//option[#selected]'));
if (empty($value) && empty($elements)) {
return ['True', true];
}
}
$currentValues = [];
if (is_bool($value)) {
$currentValues = [false];
}
foreach ($elements as $el) {
if ($el->getTagName() === 'textarea') {
$currentValues[] = $el->getText();
} elseif ($el->getTagName() === 'input' && $el->getAttribute('type') === 'radio' || $el->getAttribute('type') === 'checkbox') {
if ($el->getAttribute('checked')) {
if (is_bool($value)) {
$currentValues = [true];
break;
} else {
$currentValues[] = $el->getAttribute('value');
}
}
} else {
$currentValues[] = $el->getAttribute('value');
}
}
return [
'Contains',
$value,
$currentValues,
"Failed testing for '$value' in $strField's value: " . implode(', ', $currentValues)
];
}
You should change it to:
protected function proceedSeeInField(array $elements, $value)
{
$strField = reset($elements)->getAttribute('name');
if (reset($elements)->getTagName() === 'select') {
$el = reset($elements);
$elements = $el->findElements(WebDriverBy::xpath('.//option[#selected]'));
if (empty($value) && empty($elements)) {
return ['True', true];
}
}
$currentValues = [];
if (is_bool($value)) {
$currentValues = [false];
}
foreach ($elements as $el) {
if ($el->getTagName() === 'input' && $el->getAttribute('type') === 'radio' || $el->getAttribute('type') === 'checkbox') {
if ($el->getAttribute('checked')) {
if (is_bool($value)) {
$currentValues = [true];
break;
} else {
$currentValues[] = $el->getAttribute('value');
}
}
} else {
$currentValues[] = $el->getAttribute('value');
}
}
return [
'Contains',
$value,
$currentValues,
"Failed testing for '$value' in $strField's value: " . implode(', ', $currentValues)
];
}
Now it calls getAttribute('value') just like any other input element.
The is in file WebDriver.php